I have a method which is responsible for downloading a file.
$attachment = KnowledgeDatabaseAttachments::where('id', $id)->first();
if ($attachment) {
$filesPath = storage_path('app/knowledge_database_attachments');
return response()->download($filesPath . '/' . $attachment->physical_name);
}
After download, when I try to open it (this is an error message from my OS):
Could not load image '88ebb9c0-11af-11e8-b056-b1568dc848cb.jpg'.
Error interpreting JPEG image file (Not a JPEG file: starts with 0x0a 0xff)
File is saved like so:
$filesPath = storage_path('app/knowledge_database_attachments');
$physicalName = Uuid::generate() . '.' . $file->getClientOriginalExtension();
$file->move($filesPath, $physicalName);
KnowledgeDatabaseAttachments::create([
'knowledge_database_id' => $page->id,
'name' => $file->getClientOriginalName(),
'physical_name' => $physicalName
]);
File exist in that directory, and the downloaded file has correct size and name.
Funny part is that I can also create a newsletter which will include this file. When I create newsletter file is copied:
$extension = explode('.', $attachment->physical_name)[1];
$newPhysicalName = Uuid::generate() . '.' . $extension;
File::copy($attachment->getPathAttribute(), $storagePath . DIRECTORY_SEPARATOR . $newPhysicalName);
SendMailAttachments::create([
'mail_id' => $mail->id,
'filename' => $attachment->name,
'physical_name' => $newPhysicalName,
]);
And then, in the newsletter edit view I can as well download this file, with this (identical as above) method:
$attachment = SendMailAttachments::where('mail_id', $mailId)->where('filename', $attachmentName)->first();
if ($attachment) {
$filesPath = storage_path('app/sendmail_attachments');
return response()->download($filesPath . '/' . $attachment->physical_name);
}
And it works - file is correctly downloaded and I can open it.
Why I cant open file downloaded with first method?
I use Laravel 5.1 and Ubuntu 16.04 (if that matters).
EDIT
When I run file command on a downloaded file the result is data. When I run it on file in storage, the result is correct JPEG image data.
Try to add headers with response
View docs
$headers = array('Content-Type' => ' image/jpeg');
$filesPath = storage_path('app/knowledge_database_attachments');
return response()->download($filesPath,$attachment->physical_name,$headers);
Note: Symfony HttpFoundation, which manages file downloads, requires the file being downloaded to have an ASCII file name.
The problem is that I have something being output before the image stream.
Temporary solution:
$response = response()->download($filesPath . '/' . $attachment->physical_name);
ob_end_clean();
return $response;
Permanent solution:
Find whats being output and remove it.
Found this here: https://laracasts.com/discuss/channels/laravel/image-is-being-thrown-as-a-white-blank-image?page=1
Related
I need assistance to more understand the concept so I can become a better developer. I want to learn how to refactor the code and erase all duplications.
What's the best practices for image uploads? Renaming them correctly?
I have a block of code that handles two attachments:
if( $request->hasFile('LFImage') ) {
$destination = public_path('app/lostFound/lostItems' . $lostFound->LFImage);
if( File::exists($destination) )
{
File::delete($destination);
}
$file = $request->file('LFImage');
$extension = $file->getClientOriginalExtension();
$filename = $lostFound->LFNumber . '-' . $lostFound->lostItem . '.' . $extension;
$file->move('app/lostFound/lostItems', $filename);
$lostFound->LFImage = $filename;
}
if( $request->hasFile('handoverStatement') ) {
$destination = public_path('app/lostFound/handoverStatements' . $lostFound->handoverStatement);
if( File::exists($destination) )
{
File::delete($destination);
}
$file = $request->file('handoverStatement');
$extension = $file->getClientOriginalExtension();
$filename = $lostFound->lostItem . '-' . $lostFound->LFNumber . '.' . $extension;
$file->move('app/lostFound/handoverStatements', $filename);
$lostFound->handoverStatement = $filename;
}
They're exactly the same except with the upload directory.
How can I make it as a one code block across the entire application with changeable file name and location depending on the form?
Some file names require random strings, how can I "Edit" the random string to the file that was uploaded?
Best practice when uploading and storing files in Laravel is using Storage.
It has all needed methods to work with files, you can save the file like this:
use Illuminate\Support\Facades\Storage;
Storage::put('images/', $request->file('LFImage'));
In the documentation provided above, you can find other examples like renaming and moving files
In order to access these files from web as well, you can use the command php artisan storage:link, which creates a symbolic link to storage folder in your public folder. After you create the symbolic link, you can generate URL to the file like this:
asset('storage/test.txt')
To avoid duplications, you can create a function in your controller to create a file. You will then just call this function with different files to keep the file creation code in one place.
you can simply write this
if ($request->hasFile('logo')) {
deleteImageFromDirectory(setting('logo'), "Settings");
$data['logo'] = uploadImageToDirectory( $request->logo , "Settings");
}
and define uploadImageToDirectory function in your helper functions or create a trait
function uploadImageToDirectory($imageFile, $directory = '' ){
$imageName = $imageFile->getClientOriginalName(); // Set Image name
$imageFile->storeAs("/Images/$directory", $imageName, 'public');
return $imageName;
}
I am getting following error, when I try to save data into db after file upload:
finfo_file(/tmp/phpqE6gyD): failed to open stream: No such file or directory
This is the code:
$userFolderPath = \Yii::getAlias('#webroot') . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR . \Yii::$app->user->getIdentity()->iduser;
$model = new CsFile();
$files = UploadedFile::getInstances($model, 'files');
$errors = [];
if (!file_exists($userFolderPath))
mkdir($userFolderPath, 0777, true);
foreach($files as $file):
$fileModel = new CsFile();
$fileModel->files = $file;
if($fileModel->validate()):
$filename = str_replace(' ', '_', $file->baseName);
if(file_exists($userFolderPath . DIRECTORY_SEPARATOR . $filename . "." . $file->extension)):
$filename .= "-" .uniqid();
endif;
$fileModel->files
->saveAs($userFolderPath .DIRECTORY_SEPARATOR. $filename . '.' . $fileModel->files->extension);
$fileModel->iduser = Yii::$app->user->getIdentity()->iduser;
$fileModel->name = $filename;
$fileModel->extension = $file->extension;
$fileModel->add_date = date('Y-m-d H:i:s');
$fileModel->save();
else:
endif;
endforeach;
var_dump('<pre>', $errors, '</pre>');
I had the same problem a few weeks ago. Turns out, when we rename the file before upload and try to save the model, this error will appear.
If that attribute it's only for handle your upload and have no field in your table, you can just unset this fields before saving: $files Model->files = null.
Let me know if your scenario is different than mine.
Yii2 use UploadFile class through function $model->upload() to save upload file
To fix this use inside your $model->upload function :
return copy($this->YourAttribute->tempName, $newFileName);
instead
return $model->attribute->saveAs($newFileName)
Clyff is right. But in case you are saving the path of the file in database to read later, setting the attribute to null is not going to work.
The problem is when you try to save the model still with result of UploadedFile::getInstance($model, 'file') in the file field which is already used by $model->file->saveAs();
$model->save() cannot save the path of the saved and already removed temporary files path directly.
So after successful $model->file->saveAs($path) you need to do something like:
$model->file = $path;
It was quite unclear to me and spent a bit of time after fileinfo , so hope the answer helps.
I was having same problem, I solved it with this:
$model->file->saveAs($filepath , false)
then...
$model->save(false)
Important: In the saveAs function pass false parameter.
Using false parameter in $model->save(false) that means you are ignoring model validation, which is not right.
But using false as a second parameter in $file->saveAs($path,false) means you are trying to keep the file in the temp folder after being uploaded and allow the model to access the file during validation when trying to save to the database.
If the model fails to access the file (i.e removed from the temp folder after being uploaded), you will getting an ERROR Fail to open a stream, No such file/folder
$html = $this->load->view('pdf_output_order_details', $pdf, true);
$pdfFilePath = $pdf['data'][0]->first_name . "_" . $pdf['data'][0]->last_name . ".pdf";
ini_set('error_reporting', E_STRICT);
$this->pdf = $this->m_pdf->load('A4-L');
$this->pdf->WriteHTML($html);
$this->pdf->Output($pdfFilePath, "F");
While the pdf file is creating successfully if i change to "F" to "D"
But when attchment comes into picture then it throws an error....
"mPDF error: Unable to create output file: abc.pdf"....
I have set all permission to mpdf lib folder and n number of things done but still it won't work.... Please help guys....
Thank You....
I think your $pdfFilepath should containg not only filename, but filepath too.
From mPdf documentation:
F: save to a local file with the name given by filename (may include a
path).
Try this
$pdfFilePath = $_SERVER['DOCUMENT_ROOT'] . '/files/' . $pdf['data'][0]->first_name . "_" . $pdf['data'][0]->last_name . ".pdf";
Of course make sure you have write access to the files folder.
I have successfully uploaded a file into Google Drive. However, I'm still not sure on how to upload it into a folder. I need to upload it into a folder structure which looks like this:
Stats
ACLLeauge
ACLSydney
Sorted
Unsorted
{Username}
{FileHere}
The {Username} field is a variable that I will pass through. The {FileHere} field is where the image needs to go. Here is my current code:
public function __construct()
{
$this->instance = new \Google_Client();
$this->instance->setApplicationName('DPStatsBot');
$this->instance->setDeveloperKey(Config::getInstance()->getDriveDeveloperKey());
$this->instance->setAuthConfigFile(Config::getInstance()->getClientSecret());
$this->instance->addScope('https://www.googleapis.com/auth/drive');
if(!file_exists(DP_STATS_BOT_DIR . '/' . Config::getInstance()->getAuthFile())) {
Printer::write('Please navigate to this URL and authenticate with Google: ' . PHP_EOL . $this->instance->createAuthUrl());
Printer::raw('Authentication Code: ');
$code = trim(fgets(STDIN));
$token = $this->instance->authenticate($code);
file_put_contents(DP_STATS_BOT_DIR . '/' . Config::getInstance()->getAuthFile(), $token);
Printer::write('Saved auth token');
$this->instance->setAccessToken($token);
}
else
{
$this->instance->setAccessToken(file_get_contents(DP_STATS_BOT_DIR . '/' . Config::getInstance()->getAuthFile()));
}
if($this->instance->isAccessTokenExpired())
{
$this->instance->refreshToken($this->instance->getRefreshToken());
file_put_contents(DP_STATS_BOT_DIR . '/' . Config::getInstance()->getAuthFile(), $this->instance->getAccessToken());
}
$this->drive_instance = new \Google_Service_Drive($this->instance);
}
public function upload($image, $dpname)
{
$file = new \Google_Service_Drive_DriveFile();
$file->setTitle($dpname . '_' . RandomString::string() . '.jpg');
$upload = $this->drive_instance->files->insert($file,
[
'data' => $image,
'mimeType' => 'image/jpg',
'uploadType' => 'media'
]);
return $upload;
}
If anyone has a suggestion please tell me!
Thanks
For this you have insert the folders in the order you wanted. So add the Stats under the Drive root folder and then add all the folders in the order you needed. For adding a folder, you need to give mimeType as 'application/vnd.google-apps.folder'. Check this link for more mimeType values. Here is an external referring link on how to insert a folder in Drive.
After adding all the required folders you can now insert the actual file under the {Username} folder. You can also refer to this page on how to insert a file in Drive.
Hope that helps!
I am trying to write a script in Yii for downloading files. view file isecho CHtml::link('Major Oilseeds: World Supply and Distribution.','downloadpdf', array('class'=>'btn btn-darkorange')); and the controller code is $path = Yii::app()->request->hostInfo . Yii::app()->request->baseURL . '/download/Major_Oilseeds.csv';
echo $path;
if(file_exists($path)){
Yii::app()->getRequest()->sendFile( 'Major_Oilseeds.csv' , file_get_contents($path) );
}
else{
echo '<br>File not found';
} the code echo $path dispalys the location as http://localhost/projectv2/download/Major_Oilseeds.csv and the download folder contains the file named "Major_Oilseeds.csv" but its always showing "File Not Found" Error. plz somebody help me to solve this. I have also tried the code $path = Yii::app()->request->hostInfo . Yii::app()->request->baseURL . '/download/Major_Oilseeds.csv';
// $filename = 'Major_Oilseeds.csv';
header('Content-Disposition: attachment; charset=UTF-8; filename="'.$path.'"');
$utf8_content = mb_convert_encoding($content, "SJIS", "UTF-8");
echo $utf8_content;
Yii::app()->end();
return; but its also not working :-(
file_exists can only work with local files. In your case you are trying to use file_exists with a URL.
You can find a workaround here: http://uk1.php.net/file_exists (please search for "http" in this page)