Path of the file stored in s3 does not match with provided - Using Laravel - 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);

Related

How to edit picture on Coudinary with Laravel

Any Idea how to edit pictures on Cloudinary using Laravel API? I did a lot of searches, but I didn't find any references. The add worked successfully, but I didn't find a solution for the edit.
Add code
$user->user_image = Cloudinary::upload(
$request->file('user_image')->getRealPath(),
[
'folder' => 'Testing'
]
)->getSecurePath();
Attempt at updating picture
public function updatePicture(Request $request, $user_id)
{
$data = $request->validate([
'user_image' => '',
]);
$data = Cloudinary::upload(
$request->file('user_image')->getRealPath(),
[
'folder' => 'Testing'
]
)->getSecurePath();
User::where("user_id", $user_id)->update($data);
return response()->json([
'message' => 'Successfully updated Picture!',
'success' => true,
], 200);
}
For deleting, you can use the destroy() method of the API, for example:
$result = Cloudinary::destroy(
$public_id, //Public ID of the file from Cloudianry to delete - returned from the Upload API response
[
'...' => '...' //any optional parameters
]
)
For a list of all optional parameters and possible values please see:
https://cloudinary.com/documentation/image_upload_api_reference#destroy_method
In terms of updating, I am assuming you are referring to Cloudinary's update details of a single resource method of the Admin API. If so, you can access it like so:
$admin_api = Cloudinary::admin();
$result = $admin_api->update($public_id, $options = []);
Alternatively, if you're referring to the explicit method of the Upload API then you could access it like so:
$result = Cloudinary::explicit($public_id, $options = []);

Generating and downloading a text file on the fly in Laravel

My data is stored in the database and I need to give it out of it in a text file. There is no point in creating a file on disk, but an attempt to generate a response with a file does not work. Found a way like this but it doesn't work ERR_INVALID_RESPONSE
$contents = 'My data from db';
$filename = 'example.txt';
return response()->streamDownload(function () use ($contents) {
echo $contents;
}, $filename);
In your route name the route with something reasonable like:
Route::get('filename.txt', [FooController::class, 'foo']);
And in your controller at foo() action:
$headers = [
'Content-Type' => 'application/plain',
'Content-Description' => 'File name',
];
$contents = 'My data from db';
return \Response::make($contents, 200, $headers);

File upload corruption

Im trying to upload a file to sharepoint
Successful try with just axios is the following
Failure if i upload using Guzzle
Uploaded file at the end is corrupted
There are a few things you need to modify to get this working (tested on my end):
To get the contents of the file, you need $request->file('file') and then use file_get_contents() on it. You can lose the get() part.
Make sure you're sending the header to accept multipart/form-data too:
"Accept" => "multipart/form-data"
Fields name and filename in a form are two different things. Former is the name of the field while the latter is the name of the file. You need to send both.
Try this:
protected function uploadFile(Request $request){
$file = $request->file('file');
$body = [
"headers" => [
"Accept" => "multipart/form-data",
"Authorization" => "Bearer {$this->token}"
],
"multipart" => [
"name" => "file",
"contents" => file_get_contents($file),
"filename" => $file->getClientOriginalName()
]
];
return (new Client)->request('POST', 'https://.sharepoint.com/...', $body);
}
P.S. - You can check whether a file is valid or not using isValid():
if ($request->file('file')->isValid()) {
//
}
Official docs on this: https://laravel.com/docs/7.x/requests#retrieving-uploaded-files
When providing content to be uploaded to Guzzle client as string,
Guzzle tries to infer necessary information about the file such as filename, content-type.
You can help Guzzle to infer these information correctly to build the multipart request by passing information about about the filename and content-type in the multipart payload.
[
//...
'multipart' => [
[
'name' => 'fileName',
'contents' => $request->file('file')->get(),
'filename' => $request->file('file')->getName(),
'headers' => [
'content-type' => $request->file('file')->getMimeType(),
]
]
]
]
Add content-type as blob.
axios.post(
'https://sharepoint....'
,data.get('file')
{
'headers' {
'Authorization': `Bearer ${this.token}`
,'Content-Type': 'blob',
}
}
)
Try something like this.
Remove the ->get() of your $request->file()
//Get the file object from the request.
$file = $request->file('file');
//Make the request
return (new Client)->request('POST', 'https://.sharepoint.com/sites....', [
'headers' => [ 'Authorization' => "Bearer {$this->token}" ],
'multipart' => [
[
'name' => 'FileContents',
'contents' => $file,
'filename' => $file->getClientOriginalName()
],
],
]);
In my opinion, you should store it somewhere in your local server temporarily first, and then you sent that file to sharepoint, deleted the temporary file, I think if you send it right away without storing it, the file will be corrupted. Please try the way below
$file = $request->file('logo');
$original_name = $file->getClientOriginalName(); // get original file
$name = time() . '_' . $original_name; // store it in different name so it would not be duplicated
$path = base_path() .'/public_html/your_project/public/temporary/'; // full path of file folder
// store the uploaded file
$file->move($path, $name);
$body = [
"headers" => [
"Authorization" => "Bearer {$this->token}"
],
"multipart" => [
"name" => "logo",
"contents" => fopen($path . $name, 'r')
]
];
$response = (new Client)->request('POST', 'https://.sharepoint.com/...', $body);
// remove the file after done
unlink($path . $name);
return $response;
Hope this would work for you! Please correct me if I was wrong

Laravel Spark upload profile picture to external driver

I want to override the way Laravel Spark save the profile picture of a user to use an external driver such as S3 for example. I already have my S3 config for the bucket I want to use. What would be the best way to do this? Should I use a completely different route and use a custom endpoint or is there a config somewhere I could change so that Spark uses a different driver?
So ended up doing this
I added these methods in update-profile-photo.js
methods: {
updateProfilePhoto() {
axios.post('/settings/profile/details/profile-picture', this.gatherFormData())
.then(
() => {
console.log('Profile picture updated');
Bus.$emit('updateUser');
self.form.finishProcessing();
},
(error) => {
self.form.setErrors(error.response.data.errors);
}
);
},
gatherFormData() {
const data = new FormData();
data.append('photo', this.$refs.photo.files[0]);
return data;
}
}
And my Controller looked like this
public function updateProfilePicture(Request $request)
{
$this->validate($request, [
'photo' => 'required',
]);
// Storing the photo
//get filename with extension
$filenamewithextension = $request->file('photo')->getClientOriginalName();
//get filename without extension
$filename = pathinfo($filenamewithextension, PATHINFO_FILENAME);
//get file extension
$extension = $request->file('photo')->getClientOriginalExtension();
//filename to store
$filenametostore = $filename.'_'.time().'.'.$extension;
Storage::disk('s3_users')->put($filenametostore, fopen($request->file('photo'), 'r+'), 'public');
$url = $filenametostore;
$request->user()->forceFill([
'image_url' => $url
])->save();
return response()->json(
array(
"message" => "Profile picture was updated!",
)
);
}

Laravel : Force the download of a string without having to create a file

I'm generating a CSV, and I want Laravel to force its download, but the documentation only mentions I can download files that already exist on the server, and I want to do it without saving the data as a file.
I managed to make this (which works), but I wanted to know if there was another, neater way.
$headers = [
'Content-type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="download.csv"',
];
return \Response::make($content, 200, $headers);
I also tried with a SplTempFileObject(), but I got the following error : The file "php://temp" does not exist
$tmpFile = new \SplTempFileObject();
$tmpFile->fwrite($content);
return response()->download($tmpFile);
Make a response macro for a cleaner content-disposition / laravel approach
Add the following to your App\Providers\AppServiceProvider boot method
\Response::macro('attachment', function ($content) {
$headers = [
'Content-type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="download.csv"',
];
return \Response::make($content, 200, $headers);
});
then in your controller or routes you can return the following
return response()->attachment($content);
A Laravel 7 approach would be (from the docs):
$contents = 'Get the contents from somewhere';
$filename = 'test.txt';
return response()->streamDownload(function () use ($contents) {
echo $contents;
}, $filename);
Try this:
// Directory file csv, You can use "public_path()" if the file is in the public folder
$file= public_path(). "/download.csv";
$headers = ['Content-Type: text/csv'];
//L4
return Response::download($file, 'filename.csv', $headers);
//L5 or Higher
return response()->download($file, 'filename.csv', $headers);

Resources