File upload corruption - laravel

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

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
);

Laravel : I received song file "form-data" from one of API then i need to forward that file to another API. Any idea to forward?

Laravel project 1 controller how to forward file to API-2
Input::file('songFile')->move("/tmp", $newname);
i used this function and store tmp location then how to use this tmp location file and forward?
In order to post a file to API endpoint, you can follow the following code.
try{
$path = 'var/www/html/myproject/public/file.txt';//your file path
if (!empty($path) && file_exists($path)) {
$guzzleResponse = $client->post($api_url, [
'multipart' => [
[
'name' => 'file',// it is the name as specfied in the payload of api_url
'contents' => fopen($path, 'r')// or you can use file_get_contents()
]
],
'headers' => $headers
]);
}
if ($guzzleResponse->getStatusCode() == 200) {
$response = json_decode($guzzleResponse->getBody());// whatever you want to do with response
}
}catch(RequestException $e){
return $e; //You can also handle specific status codes here using eg $e->getResponse()->getStatusCode() == '400'
}
Also $headers can be like this
[
'Accept' => 'application/json',
'Authorization' => 'Bearer '. $userToken,
]
See more information in Guzzle

Error: Pass content as json or plain text

I am using guzzle to post data to the api as below. When i post, the api returns Error: Pass content as json or plain text. Is it possible to convert content to JSON or Plain Text in the code below
How can I resolve this?
Controller
$headers = [
'Content-Type' => 'application/x-www-form-urlencoded',
];
$client = new \GuzzleHttp\Client([
'headers' => $headers
]);
$body = '{
"item" : Summer-Jacket,
"content" : Hi
}';
$url = "https://api.com";
$request = $client->post($url,
[
'body' => $body
] );
$response = $request->send();
Firstly your body is not even valid JSON. Strings needs to have "string" around them
To post JSON with guzzle, you can instead use the RequestOptions for exactly that.
$result = $client->post("https://api.com", [
RequestOptions::JSON => [
'item' => 'Summer-Jacket',
'content' => 'hi',
]
)->getBody();

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);

form_params method get guzzle php

I have an API get list user. postmen
and Headers Content-Type = application/json
- In laravel, I use guzzle to call api
code demo:
$client = new Client();
$headers = ['Content-Type' => 'application/json'];
$body = [
'json' => [
"filter" => "{}",
"skip" => 0,
"limit" => 20,
"sort" => "{\"createAt\": 1}",
"select" => "fullname username",
"populate" => "'right', 'group'",
]
];
\Debugbar::info($body);
$response = $client->get('http://sa-vn.com:2020/api/users/user', [
'form_params' => $body
]);
echo $response->getBody();
But it does not working! please help me
form_params and body both are different params in guzzle. check json
$json = [
"filter" => json_encode((object)[]),
"skip" => 0,
"limit" => 20,
"sort" => json_encode((object)['createAt'=>1]),
"select" => "fullname username",
"populate" => "'right', 'group'"
];
$response = $client->request('get', 'http://sa-vn.com:2020/api/users/user', [
'json' => $json,
]);
If any error occur try without json_encode as well.
$json = [
"filter" => (object)[],
"skip" => 0,
"limit" => 20,
"sort" => (object)['createAt'=>1],
"select" => "fullname username",
"populate" => "'right', 'group'"
];
As per Guzzle doucmentation
form_params
Used to send an application/x-www-form-urlencoded POST request.
json
The json option is used to easily upload JSON encoded data as the body of a request. A Content-Type header of application/json will be added if no Content-Type header is already present on the message.
You are passing json data in postman. So you can use json instead of form_params
Change
$response = $client->get('http://sa-vn.com:2020/api/users/user', [
'form_params' => $body
]);
to
$response = $client->get('http://sa-vn.com:2020/api/users/user', [
'json' => $body
]);

Resources