I am trying to delete a file in Laravel but it is causing this exception:
unlink(D:\graduation project\kh\storage\app\upload/three.zip): Resource temporarily unavailable in D:\graduation project\kh\vendor\league\flysystem\src\Adapter\Local.php
before trying to delete the file i checked if it exists, checking returned a true value.
here is my piece of code in the controller:
protected function saveFile(UploadedFile $file)
{
$zip = new Zipper;
$fileName = $this->createFilename($file);
$finalPath = storage_path().'/app/upload/';
// move the file name
$file->move($finalPath, $fileName);
$zip->make($finalPath.$fileName)->extractTo($finalPath);
$exist = Storage::disk('local')->exists('/upload/'.$fileName);
if($exist){
Storage::delete('/upload/'.$fileName) //I get the error here;
}
return response()->json([
'path' => $finalPath,
'name' => $fileName,
'exist' => $exist
]);
}
and this is the response I get. It shows that the file exists (Note: when I took this image I commented the deletion line): response
Related
I'm uploading a file to a file upload component, and then passing the temporary file path to the parent component with an event. In the parent component I need to save the file to s3.
I need to pass the path or a file object or something back to the parent component, and then save it, but I can't seem to get it to work.
I've tried sending over a File object, as well as an UploadedFile object, my latest iteration is to try with a File object, and I'm getting the following error:
Unresolvable dependency resolving [Parameter #0 [ <required> string $path ]] in class Symfony\Component\HttpFoundation\File\File
So in my child component I have this code:
public function updatedFile()
{
$fileObj = new File($this->file->path());
$this->emitUp('fileUploaded', $fileObj);
}
In my parent component I'm listening for the fileUploaded event, which calls the save method:
public function save(File $uploadedFile)
{
if ($path = Storage::putFileAs(env('APP_ENV') . '/statements', $uploadedFile->name, 's3')) {
$this->statement = new Statement([
'location_id' => $this->location->id,
'file_name' => $uploadedFile->name,
'path' => $path,
'uploaded_by' => Auth::user()->id,
]);
$this->statement->save();
}
}
I've also tried using $uploadedFile->storeAs() and I get the same result. It seems like the $uploadedFile object is not the right type. I don't know if I need a Storage object or what and I can't seem to find a good answer in the docs.
The path I have available after uploading the file in my livewire component is the temporary file name that livewire saves the file as in local storage. I also need the original file name as well, like what was uploaded as I'm saving that to the database.
If I remove the type hint on the save() method I get Attempt to read property "name" on array. Why is $uploadedFile an array and not an object? I guess if I remove the type hint it just gets sent over as an array. I dunno..
Here's the solution I came up with:
child component:
public function updatedFile()
{
$this->validate([
'file' => 'required|max:12288'
]);
$this->emitUp('fileUploaded', [$this->file->path(), $this->file->getClientOriginalName()]);
}
parent component:
public function save($uploadedFile)
{
if ($path = Storage::disk('s3')->put(env('APP_ENV') . '/statements/' . $uploadedFile[1], file_get_contents($uploadedFile[0]))) {
$this->statement = new Statement([
'location_id' => $this->location->id,
'file_name' => $uploadedFile[1],
'path' => $path,
'uploaded_by' => Auth::user()->id,
]);
$this->statement->save();
}
}
I want the uploaded file to be located in the public/uploads folder directly like public/uploads/my_file.jpeg. Why is it that my code uploads it to public/uploads/file_name/file.jpeg?
here is the filesystems.php.
'public_uploads' => [
'driver' => 'local',
'root' => public_path() . '/uploads',
],
and here is the controller.
function upload_template(Request $request)
{
$filee = $request->file('file_upload');
$file_ext = $filee->extension();
$file_name = $model->id . "." . $file_ext;
Storage::disk('public_uploads')->put($file_name, $filee);
}
This happened because you specify the directory to store as filename. The file_name, should be the directory name such as images.
Refer to this line :
Storage::disk('public_uploads')->put($file_name, $filee);
So you could change this to :
Storage::disk('public_uploads')->put('images', $filee);
// output : /images/234234234.jpg
You need to provide the file contents in the second argument not file object, try this :
Storage::disk('public_uploads')->put($file_name, file_get_contents($filee));
To specific the file name you can use move() method instead of storage() :
if($request->hasFile('file_upload'))
{
$filee = $request->file_upload;
$name = "my_file"; // name here
$fileName = $name . $filee->getClientOriginalName();
$filee->move('public_uploads',$fileName);
}
//this is the best way you create a trait with 2 functions saveImage and
//deleteImage
public function saveImage($name,$folder){
$extention=$name->getClientOriginalExtension();
$filename=time().'.'.$extention;
$path=public_path().'/'.$folder;
$name->move($path,$filename);
return $filename;
}
public function deleteImage($name,$folder){
$image_path=public_path().'/'.$folder.'/'.$name;
unlink($image_path);
}
function upload_template(Request $request){
$file = $request->file_upload;//$request->your input name
$img=$this->saveImage($file,'uploads');
//you can use $img for storing the image in database for example
User::create([
'avatar'=>$img
])
}
//don't forget to invoke your trait
I just found it Laravel 5.3 Storage::put creates a directory with the file name.
Need to provide the file contents in the second argument not file object.
Tt should be Storage::disk('public_uploads')->put($file_name, file_get_contents($filee));.
I have created my own 'disk' in config/filesystems.php which looks like so;
'uploads' => [
'driver' => 'local',
'root' => storage_path('app/public') . '/uploads'
],
This seems to work fine when in my controller, it uploads the file and returns a response. my code is as follows;
public function store(Request $request)
{
if ($request->hasFile('filename')) {
foreach ($request->file('filename') as $image) {
$fileName = md5($image . microtime()) . '.' . $image->getClientOriginalExtension();
$image->storeAs('', $fileName, 'uploads');
}
}
// return goes here
}
but when I go to test my store method using the following code;
public function testUserCanSuccessfullySubmitSingleImage(): void
{
Storage::fake('uploads');
$this->postJson('/upload', [
'filename' => UploadedFile::fake()->image('image1.jpg')
]);
Storage::disk('uploads')->assertExists('image1.jpg');
Storage::disk('uploads')->assertMissing('missing.jpg');
}
I get the following error;
Unable to find a file at path [image1.jpg].
Failed asserting that false is true.
I have followed a few tutorials, but they all say the same thing and im really lost.
Any help would be greatly appreciated.
Cheers
The problem is that you are renaming your file in the controller with md5($image . microtime()) so you cannot assert that image1.jpg exists since you changed the name.
What you could do is let laravel name the file and then check that in your test:
In your Controller:
Replace storeAs with store, store will generate a unique ID to serve as the file name.
public function store(Request $request)
{
if ($request->hasFile('filename')) {
foreach ($request->file('filename') as $image) {
$image->store('', 'uploads');
}
}
}
In your test:
To assert if the image exists we will use the same method to generate the unique ID as laravel does when saving the image. Replace 'image1.jpg' with $image->hashName() in your assertion.
public function testUserCanSuccessfullySubmitSingleImage(): void
{
Storage::fake('uploads');
$this->postJson('/upload', [
'filename' => $image = UploadedFile::fake()->image('image1.jpg')
]);
Storage::disk('uploads')->assertExists($image->hashName());
Storage::disk('uploads')->assertMissing('missing.jpg');
}
From the docs:
In web applications, one of the most common use-cases for storing
files is storing user uploaded files such as profile pictures, photos,
and documents. Laravel makes it very easy to store uploaded files
using the store method on an uploaded file instance. Call the store
method with the path at which you wish to store the uploaded file:
public function update(Request $request)
{
$path = $request->file('avatar')->store('avatars');
return $path;
}
There are a few important things to note about this example. Note that
we only specified a directory name, not a file name. By default, the
store method will generate a unique ID to serve as the file name. The
file's extension will be determined by examining the file's MIME type.
The path to the file will be returned by the store method so you can
store the path, including the generated file name, in your database.
I want to write a test for file download, for this first I'm uploading the file, then calling the API to download the uploaded file, upload is succeeded, but download always fails, and shows The file "/var/www/public/uploads/dWwECsHQpcwJuYTn6uaLmPxk4uINOeYOZYiZ86Oc.jpeg" does not exist.
Following is my test function content:
Storage::fake('public');
$business = factory(Business::class)->create(['owner_id' => $this->businessUser->id]);
$response = $this->jsonAs($this->businessUser,'POST', '/api/file/business', [
'file' => $file = UploadedFile::fake()->create('invalid file.jpg'),
'attachable_id' => $business->id,
'attachable_type' => 'businesses'
]);
$response->assertJson(['name' => $file->hashName()]);
Storage::disk('public')->assertExists('uploads/' . $file->hashName());
$uploadRes = $response->decodeResponseJson();
$response = $this->jsonAs($this->businessUser, 'GET', '/api/file/'. $uploadRes['id'] . '/business/' .$business->id);
// This assertion always fails
// If I dd above response, shows this message 'The file "/var/www/public/uploads/dWwECsHQpcwJuYTn6uaLmPxk4uINOeYOZYiZ86Oc.jpeg" does not exist'
$this->assertTrue($response->headers->get('content-type') == $file->getClientMimeType());
$this->assertTrue($response->headers->get('content-disposition') == 'attachment; filename="' . $uploadRes['original_filename'] . '"');
$response->assertStatus(200);
And following is my download function content:
$attachment = Attachment::where('id', $id)->firstOrFail();
$path = public_path(). '/uploads/' . $attachment->name;
return response()->download($path, $attachment->original_filename, ['Content-Type' => $attachment->mime]);
Make sure the file exists in the public/upload directory as well as you can generate link for public directory files using url() function.
EX:
$attachment = Attachment::where('id', $id)->firstOrFail();
$path = url('uploads/' . $attachment->name);
return response()->download($path, $attachment->original_filename, ['Content-Type' => $attachment->mime]);
In the download implementation , I noticed that public_path function is used.
public_path function resolves from the service container probably creating a new path from the real path for public disk configuration.
Storage::fake is setting disk path and returning FilesystemAdapter and you want to make sure that this is the instance used in the lifecycle for the test spec.
I suggest to try using path method from FilesystemAdapter via Storage facade to construct the path in the download implementation instead of the public_path helper function. For example:
Storage::disk('public')->path('uploads/'.$attachment->name);
I'm having what looks to me some strange behaviour within Yii.
I have a simple file upload, that takes a name and the file itself.
If I just submit the form with no name or file I can bypass the validation (i.e - My controller action is called and is trying to process the uploaded file) I think my rules() are setup accordingly to stop this. These are my relevant rules:
public function rules() {
return array(
array('name file', 'required', 'message' => 'This field is required'),
array('file', 'file', 'on' => 'insert', 'allowEmpty' => false, 'safe'=>true,
'maxSize'=> 512000,
'maxFiles'=> 1,
'mimeTypes' => 'application/msword, text/plain',
'tooLarge'=> 'file cannot be larger than 500KB.',
'wrongMimeType'=> 'Format must be: .doc .txt'
),
I specified that the file is required and also within the file array that allowEmpty should be false. So what am I doing wrong here?
Thanks in advance for any help
Controller
public function actionCreate() {
$model = new File;
if (isset($_POST['File'])) {
$model->setAttributes($_POST['File']);
// Set file
$model->file = CUploadedFile::getInstance($model,'file');
// Set directory
$dest = Yii::getPathOfAlias('application.uploads');
$model->tmp_name = time();
$model->date_added = new CDbExpression('NOW()');
$model->file_type = $model->file->type;
$model->file_size = $model->file->size;
$model->extension = $model->file->extensionName;
if ($model->save()) {
$model->file->saveAs(($dest . '/' . $model->tmp_name . '.' . $model->file->extensionName));
Yii::app()->user->setFlash('success','<strong>Success!</strong> Your file has been uploaded');
}
}
$this->render('create', array( 'model' => $model));
}
For one you're missing a , in your first rule between name and file. Then you say:
I can bypass the validation (i.e - My controller action is called ...
From that i assume you use AJAX validation and expect the upload to fail. But you can't do AJAX validation on file uploads with CActiveForm.
So if you fix the typo above, you'll at least get AJAX validation for the name attribute.
You should maybe also remove the 'on'=>'insert' scenario. And you don't need the 'safe'=>true because you don't do massive assignment with the $model->file attribute.
For me I found that if I validate before I process the uploaded file it worked. Wasn't quite sure why I had to do that as I thought the save() method automatically called the validate() method
Validation will be performed before saving the record. If the validation fails, the record will not be saved. You can call getErrors() to retrieve the validation errors.
Updated code
public function actionCreate() {
$model = new File;
if (isset($_POST['File'])) {
$model->setAttributes($_POST['File']);
if($model->validate()){ // add in validation call
// Set file
$model->file = CUploadedFile::getInstance($model,'file');
// Set directory
$dest = Yii::getPathOfAlias('application.uploads');
$model->tmp_name = time();
$model->date_added = new CDbExpression('NOW()');
$model->file_type = $model->file->type;
$model->file_size = $model->file->size;
$model->extension = $model->file->extensionName;
if ($model->save()) {
$model->file->saveAs(($dest . '/' . $model->tmp_name . '.' . $model->file->extensionName));
Yii::app()->user->setFlash('success','<strong>Success!</strong> Your file has been uploaded');
}
}
}
$this->render('create', array( 'model' => $model));
}
Hope that helps anyone, thanks to #Michael Härtl too