Trying to get a S3 image URL inside a bucket folder - laravel

I'm trying to get a proper link to be consumed from an API request. I stored an image in a S3 bucket. Here's the steps:
A user fills a form and upload an image.
Image uploaded into S3 bucket within a folder (let's say: images/mynewimage.png)
The user open a new page which contains the image
The problem is, the image URL I get from Laravel is different from the S3 bucket itself.
From S3 bucket, the url looks like this :
https://prismahrbucket.s3-ap-southeast-1.amazonaws.com/reimburses/Screen+Shot+2020-03-17+at+14.21.38.png
But from Laravel, the given URL is wrong. Like this:
https://prismahrbucket.s3-ap-southeast-1.amazonaws.com/Screen+Shot+2020-03-17+at+14.21.38.png
Please have a look at my scripts:
ReimburseController.php
/**
* Store a newly created resource in storage.
*
* #param \App\Http\Requests\RequestReimburse $request
* #return \Illuminate\Http\Response
*/
public function store(RequestReimburse $request)
{
$validated = collect($request->validated())
->except(['images'])
->toArray();
if ($request->images) {
$images = json_decode($request->images);
$paths = [];
foreach ($images as $image) {
array_push($paths, $this->handleUploads($image, $validated));
}
$validated = array_add($validated, 'images', json_encode($paths));
}
$reimburse = Reimburse::create($validated);
return response()->json([
'created' => true,
'data' => $reimburse,
], 201);
}
protected function handleUploads($image, $validated)
{
$imageName = time() . str_random(10) . '.png';
Storage::disk('s3')
->put("reimburses/{$imageName}", base64_decode($image), 'public');
$path = Storage::disk('s3')->url($imageName);
return $path;
}
filesystem.php
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'visibility' => 'public' // Make s3 files public
],
How can I solve it?

I've just found a solution. Instead of using only $imageName, I specified the folder name at the beginning.
I mean, I changed this:
$path = Storage::disk('s3')->url($imageName);
To this: $path = Storage::disk('s3')->url("images/$imageName");

Related

upload file on s3 with quasar vue (frontend) and laravel 9 (backend)

I'm building a platform and I need to upload resources (pdf, images etc) on amazon s3 bucket.
Frontend is made with quasar vue, backend with Laravel 9. The idea is to send file from frontend to backend and upload file on s3 with laravel.
The problem is that nothing happens, my s3 bucket remains empty with no errors or exceptions.
I'm not sure if the problem is sending file from frontend to backend or on the upload from laravel to s3 bucket.
I use q-uploader (but I think is not too different with quasar file-picker). Here's frontend code
<template>
<q-page class="q-pa-sm">
<q-uploader :factory="uploadFile"/>
</q-page>
</template>
<script>
import { api } from 'src/boot/axios';
export default {
methods: {
uploadFile(f){
var fileModel = f[0]
const fileData = new FormData()
fileData.append('file', fileModel)
api.post('/api/resources/file', fileData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(res=>{
console.log(res)
})
}
}
}
</script>
I made my API on api.php (and I'm sure middleware is not the problem)
Route::prefix('resources')->group(function(){
/*
* other APIs
*/
Route::post('/file',[ResourceController::class, 'postResourceFile'])->middleware('roles:1|2|3');
});
In laravel I've got a controller called ResourceController.php with this function
public function postResourceFile(Request $request){
if($request->hasFile('file')){
$file = $request->file('file');
$name=time().$file->getClientOriginalName();
$filePath = 'resources/' . $name;
Storage::disk('s3')->put($filePath, file_get_contents($file));
$response = [
'file' => $file,
];
} else {
$response = [
'message' => 'no file'
];
}
return response($response, 200);
}
In Laravel filesystems.php I didn't change original configuration
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
],
And here's my .env AWS configuration.
AWS_ACCESS_KEY_ID=myID
AWS_SECRET_ACCESS_KEY=myKey
AWS_DEFAULT_REGION=eu-central-1
AWS_BUCKET=myBucket
AWS_USE_PATH_STYLE_ENDPOINT=false
AWS_URL=http://s3.eu-central-1.amazonaws.com/myBucket
Backend response is
file:{}
but I'm not sure if $file is empty or it'is just a browser problem, because file should be something like blob.
Can someone helps me?
Thanks in advance
File is a blob and cannot be converted to Json easily.
I'm almost sure your code works.
to check that, you can log the file:
Log::debug('file received => ', ['file' => $request->file('file')]);
Double check if laravel is putting the file into s3. I think the way you're doing maybe it's incorrect. The way i do is:
// on the top:
use Illuminate\Http\File;
use Illuminate\Http\Request;
// on the function:
$file = new File($request->file('file'));
$path = Storage::disk('s3')->putFile('resources', $file);
return response()->json(
[
'status' => 'success',
"url" => Storage::url($path),
'size' => Storage::size($path),
'type' => $file->extension()
],
200
);

How can i change the image upload directory and view image url in laravel

In my script all images uploaded goes into directory "ib" but i want to change that directory to a different name eg. "imgib"
here is what i did so far. i changed the code vlues from "ib" to "imgib"
} else {
// upload path
$path = 'imgib/';
// if path not exists create it
if (!File::exists($path)) {
File::isDirectory($path) or File::makeDirectory($path, 0777, true, true);
}
// move image to path
$upload = $request->file('uploads')->move($path, $imageName);
// file name
$filename = url($path) . '/' . $imageName;
// method server host
$method = 1;
}
// if image uploded
if ($upload) {
// if user auth get user id
if (Auth::user()) {$userID = Auth::user()->id;} else { $userID = null;}
// create new image data
$data = Image::create([
'user_id' => $userID,
'image_id' => $string,
'image_path' => $filename,
'image_size' => $fileSize,
'method' => $method,
]);
// if image data created
if ($data) {
// success array
$response = array(
'type' => 'success',
'msg' => 'success',
'data' => array('id' => $string),
);
// success response
return response()->json($response);
} else {
if (file_exists('imgib/' . $filename)) {$delete = File::delete('imgib/' . $filename);}
// error response
return response()->json(array(
'type' => 'error',
'errors' => 'Opps !! Error please refresh page and try again.',
));
}
so far everything looks ok and it creates "imgib" directory automatically and all uploads go into "imgib" directory.
But the issue is, image url still uses the same old directory name.
eg. site.org/ib/78huOP09vv
How to make it get the correct url eg. site.org/imgib/78huOP09vv
Thanks for the help everyone. I managed to fix the issue by editing viewimage.blade.php
Yes of course need to clear the browser cache after editing the files.

Path of the file stored in s3 does not match with provided - Using Laravel

I'm building a service to upload images with laravel and stored in a aws s3 bucket, this is the function responsible for store image.
public function fromUrl(Request $request)
{
$validator = Validator::make($request->all(), [
'files' => 'required|array|min:1',
'files.*' => 'string',
]);
if (!$validator->fails()) {
$paths = [];
foreach ($validator->validate()['files'] as $file) {
$url = config('services.s3.host') . Storage::disk('s3')->put('images/public', file_get_contents($file), 'public');
array_push($paths, $url);
}
return $paths;
} else {
throw new ValidateException([
'message' => $validator->errors()->toArray(),
'rules' => $validator->failed()
]);
}
}
The request body looks like this.
{
"files": [
"https://image-url-1",
"https://image-url-2"
]
}
I expect that the path returned when saving the image is something like this.
[
"https://my-bucket-url/images/public/random-name-for-image1",
"https://my-bucket-url/images/public/random-name-for-image2"
]
but instead I'm getting the following.
[
"https://my-bucket-url/1",
"https://my-bucket-url/1"
]
You are misusing put in your example.
Firstly the first parameter is the path plus filename and you have no filename random logic. The third parameter is options array.
$randomFileName = uniqid(rand(), true);
$path = 'images/public/' . $randomFileName;
Storage::disk('s3')->put($path, file_get_contents($file));
This code will save an element at images/public/$randomFileName. To return the proper path you can use the url() method.
$url = Storage::disk('s3')->url($path);
array_push($paths, $url);

Database record not being created when large file is uploaded

I have a decoupled VueJS frontend and a Laravel API backend.
When I upload a large file (3GB for instance) there's a Eloquent create() method that doesn't get executed. However, when I upload smaller files (a few hundred MB) it executes without problem.
My code:
public function store(Request $request){
if(count($request->file('files'))){
$shareId = substr(md5(rand()), 0, 7);
try{
$user = User::where('name', 'Anon')->first(); //save file anonymously
$upload = $user->uploads()->create(['share_id' => $shareId, 'visibility' => 'public']);
foreach($request->file('files') as $file){
\Log::info('before file store');
$disk = Storage::disk('files');
$disk->put($file->getClientOriginalName(), fopen($file, 'r+'));
\Log::info('after file
\Log::info('before db create');
//the below code and everything after it doesn't execute
$upload->files()->create(['name' => 'testname',
'originalName' => $file->getClientOriginalName(),
'size' => $file->getClientSize(),
'path' => 'test', 'visibility' => 'public',
'type' => $file->getClientOriginalExtension()]);
\Log::info('after db create');
}
return response()->json([
"success" => $shareId
]);
}
catch(\Exception $e){
return response()->json([
"error" => $e
]);
}
}
}
I log some text to see what executes and only the below logs are shown:
[2019-09-27 14:15:52] local.INFO: before file store
[2019-09-27 14:15:52] local.INFO: after file store
[2019-09-27 14:15:52] local.INFO: before db create
What is causing this? My PHP server is configured to allow files of up to 5GB to be uploaded. I've also set my max_execution_time to 10mins to test and the problem persists. I've omitted the Vue code as I believe this is a backend issue.

Upload photo in Laravel

Laravel file upload gives the user error when trying to upload images
Get error: local.ERROR: Driver [] is not supported.
How to fix its problem?
public function channelAvatar(Request $request, Channel $channel)
{
// validate
$this->validate($request, [
'photo' => ['required', 'image', Rule::dimensions()->minWidth(250)->minHeight(250)->ratio(1 / 1)],
]);
// fill variables
$filename = time() . str_random(16) . '.png';
$image = Image::make($request->file('photo')->getRealPath());
$folder = 'channels/avatars';
// crop it
$image = $image->resize(250, 250);
// optimize it
$image->encode('png', 60);
// upload it
Storage::put($folder.'/'.$filename, $image->__toString());
$imageAddress = $this->webAddress() . $folder . '/' . $filename;
// delete the old avatar
Storage::delete('channels/avatars/' . str_after($channel->avatar, 'channels/avatars/'));
// update channel's avatar
$channel->update([
'avatar' => $imageAddress,
]);
$this->putChannelInTheCache($channel);
return $imageAddress;
}
Uploading locally or to FTP still gives the same error.
Whenever you use a Storage option without a specific disk, Laravel uses the default driver. It seems like you have specified local as default driver but you do not have it configured.
As per your config/filesystem.php you have :
'ftp' => [
'driver' => 'ftp',
'host' => env('FTP_HOST', 'test.something.net'),
'username' => env('FTP_USERNAME', 'someusername'),
'password' => env('FTP_PASSWORD', '*******'),
],
So you need to specify this as a default driver. You can do that by adding :
FILESYSTEM_DRIVER=ftp inside the .env file.
And then inside the 'config/filesystem.php` add following :
'default' => env('FILESYSTEM_DRIVER', 'local'),
Now whenever you do Storage::something() it will use default driver. (Something you will have local as the default one`
You can also specify it if you would like :
Storage::disk('ftp')->something() But if your all storage operations use one disk then better specify as default.

Resources