Laravel 4 - Rename downloaded files on the fly - php

I have a form on my app that allows users to upload a file .. That file gets given a random name, saved to the file system and the details including the original name of the file and the random name of the file gets saved to the database.
When the user clicks the link to re-download the file, i want the file to be renamed to the original file name on the fly but kept as the random name in the file system... if that makes sense.
The code i'm using to download the file at the moment is as follows
// Download the document
$file = public_path() . '/uploads/' . $document->userid . '/' . $document->storedname;
return Response::download($file);
The random filename that is created on upload is $document->storedname
The original name of the file is stored as $document->originalname

Pass it as the second argument to the download method:
return Response::download($file, $document->originalname);

$newName = "download.pdf";
$filename="samplepdf.pdf";
$path = storage_path("app/public/" . $filename);
if (!\File::exists($path)) {
return response()->json(['status' => false], 404);
}
return response()->download($path,$newName);

Related

Why is Laravel renaming the file extension of the image that I upload? [duplicate]

I am allowing users to upload any kind of file on my page, but there might be a clash in names of files. So, I want to rename the file automatically, so that anytime any file gets uploaded, in the database and in the folder after upload, the name of the file gets changed also when other user downloads the same file, renamed file will get downloaded.
I tried:
if (Input::hasFile('file')){
echo "Uploaded</br>";
$file = Input::file('file');
$file ->move('uploads');
$fileName = Input::get('rename_to');
}
But, the name gets changed to something like:
php5DEB.php
phpCFEC.php
What can I do to maintain the file in the same type and format and just change its name?
I also want to know how can I show the recently uploaded file on the page and make other users download it??
For unique file Name saving
In 5.3 (best for me because use md5_file hashname in Illuminate\Http\UploadedFile):
public function saveFile(Request $request) {
$file = $request->file('your_input_name')->store('your_path','your_disk');
}
In 5.4 (use not unique Str::random(40) hashname in Illuminate\Http\UploadedFile). I Use this code to ensure unique name:
public function saveFile(Request $request) {
$md5Name = md5_file($request->file('your_input_name')->getRealPath());
$guessExtension = $request->file('your_input_name')->guessExtension();
$file = $request->file('your_input_name')->storeAs('your_path', $md5Name.'.'.$guessExtension ,'your_disk');
}
Use this one
$file->move($destinationPath, $fileName);
You can use php core function rename(oldname,newName) http://php.net/manual/en/function.rename.php
Find this tutorial helpful.
file uploads 101
Everything you need to know about file upload is there.
-- Edit --
I modified my answer as below after valuable input from #cpburnz and #Moinuddin Quadri. Thanks guys.
First your storage driver should look like this in /your-app/config/filesystems.php
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'), // hence /your-app/storage/app/public
'visibility' => 'public',
],
You can use other file drivers like s3 but for my example I'm working on local driver.
In your Controller you do the following.
$file = request()->file('file'); // Get the file from request
$yourModel->create([
'file' => $file->store('my_files', 'public'),
]);
Your file get uploaded to /your-app/storage/app/public/my_files/ and you can access the uploaded file like
asset('storage/'.$yourModel->image)
Make sure you do
php artisan storage:link
to generate a simlink in your /your-app/public/ that points to /your-app/storage/app/public so you could access your files publicly. More info on filesystem - the public disk.
By this approach you could persists the same file name as that is uploaded. And the great thing is Laravel generates an unique name for the file so there could be no duplicates.
To answer the second part of your question that is to show recently uploaded files, as you persist a reference for the file in the database, you could access them by your database record and make it ->orderBy('id', 'DESC');. You could use whatever your logic is and order by descending order.
You can rename your uploaded file as you want . you can use either move or storeAs method with appropiate param.
$destinationPath = 'uploads';
$file = $request->file('product_image');
foreach($file as $singleFile){
$original_name = strtolower(trim($singleFile->getClientOriginalName()));
$file_name = time().rand(100,999).$original_name;
// use one of following
// $singleFile->move($destinationPath,$file_name); public folder
// $singleFile->storeAs('product',$file_name); storage folder
$fileArray[] = $file_name;
}
print_r($fileArray);
correct usage.
$fileName = Input::get('rename_to');
Input::file('photo')->move($destinationPath, $fileName);
at the top after namespace
use Storage;
Just do something like this ....
// read files
$excel = $request->file('file');
// rename file
$excelName = time().$excel->getClientOriginalName();
// rename to anything
$excelName = substr($excelName, strpos($excelName, '.c'));
$excelName = 'Catss_NSE_'.date("M_D_Y_h:i_a_").$excelName;
$excel->move(public_path('equities'),$excelName);
This guy collect the extension only:
$excelName = substr($excelName, strpos($excelName, '.c'));
This guy rename its:
$excelName = 'Catss_NSE_'.date("M_D_Y_h:i_a_").$excelName;

Ensuring random key names in S3 while maintaining original file names by user

Previously, I stored client files locally, on a server using PHP (and running Apache). They would upload files, and each one would be given a randomized string ending in a pdf / jpg file extension. The original file name would be kept in a database along with the randomized name to link them back together when the user wanted the file.
I wanted to transition to storing files on a private bucket in S3. The first thing I'm seeing is this article which says to give Object keys a unique name, but all the examples I'm seeing just put the user's file name in there.
This is an issue because if a user stores test.pdf and another, entirely different user uploads test.pdf, then it won't be uploaded. Another issue is if I use the random file names like I have previously been doing, and then the user gets the file from a pre-signed request, then they will be accessing a file named via some random string and not the file they thought they uploaded.
What should I be doing to separate out a user's files, while keeping the original file name on s3?
Personally, I do exactly what you describe in your first example. The S3 file gets a UUID generated for the file name in the bucket and all the metadata including the original file name goes in the database.
I don't even bother giving the S3 file an extension.
To expand on my comments and the question about how to read the files back;
I'm using Laravel with Intervention\Image (site).
My GET endpoint for the attachment controller returns this function in my model:
/**
* Gets an image from Amazon and returns it
* #param boolean $thumb
* #return null|Image
*/
public function output($thumb = false)
{
if ($this->s3_filename === null) {
return null;
}
// Grab the image from S3
$this->image = $this->s3->get('/' . $this->getPath() . '/' . ($thumb ? 'thumb/' : '') . $this->s3_filename);
if ($this->image === null) {
return null;
}
return Image::make($this->image)->response()->withHeaders([
'content-disposition' => 'inline; filename="' . ($thumb ? 'thumb_' : '') . $this->filename . '"',
]);
}
How about considering using buckets/folders?
Buckets need to have unique names (across ALL of AWS... not sure if that has changed). But the folders within them are fine.
But otherwise:
myBucket/
user1/
test.pdf
user2/
test.pdf
There's not an additional cost to having directories within buckets AFAIK so you should be good.
You can also use a UUID instead of user1, and have a table somewhere that maps usernames to UUID to generate the bucket/folder path.

Get file from temp after confirm with PHP/Laravel

I have a form with a file to uplaod. All works find. But I don't want to move the file directly into a folder.
After submit I show a confirm page and there I show the uploaded file with
header('Content-Type: image/x-png');
$file = file_get_contents(\Illuminate\Support\Facades\Input::file('restImg'));
$imgType = \Illuminate\Support\Facades\Input::file('restImg')->guessClientExtension();
echo sprintf('<img src="data:image/png;base64,%s" style="max-height: 200px"/>', base64_encode($file));
This works fine. After the confirmation I like to move the file to a folder. How can I move the file after the confirmation? The Input::get('file') is not available anymore.
You will have to store the file in the initial upload somewhere temporarily other than the default tmp directory.
The documentation for PHP file uploads says:
The file will be deleted from the temporary directory at the end of the request if it has not been moved away or renamed
This means that moving onto the next request, the file will no longer be available.
Instead, move it to your own custom temp directory or rename it to something special, then keep the filename in the $_SESSION to persist it to the next request.
For Laravel, this should mean putting it in the /storage directory with something like this:
// Get the uploaded file
$file = app('request')->file('myfile');
// Build the new destination
$destination = storage_path() . DIRECTORY_SEPARATOR . 'myfolder';
// Make a semi-random file name to try to avoid conflicts (you can tweak this)
$extension = $file->getClientOriginalExtension();
$newFilename = md5($file->getClientOriginalName() . microtime()).'.'.$extension;
// Move the tmp file to new destination
app('request')->file('myfile')->move($destination, $newFilename);
// Remember the last uploaded file path at new destination
app('session')->put('uploaded_file', $destination.DIRECTORY_SEPARATOR.$newFilename);
Just remember to unlink() the file after the second request or do something else with it, or that folder will fill up fast.
Additional Reference:
http://api.symfony.com/2.7/Symfony/Component/HttpFoundation/File/UploadedFile.html

Changing the filename of a file stored on Amazon S3 upon download

So I have an upload service with many people uploading the same files to my Amazon S3 bucket. I changed my app design so the SHA1 of the file is calculated upon upload and checked against the list of uploaded files.
If it exists, I simply assign the file to the new uploader as well.
The problem with this is, the file being named as the first uploader named it. All subsequent uploaders will get the same first name.
I can use download="" attribute in HTML5 but it doesn't work in IE:
http://caniuse.com/#search=download
The files are stored remotely so I can't change the header unless I download it first to my local server which is illogical.
Please advice.
The only way I see this possible is to add a meta-data just before redirecting user to download.
You can add meta-data to your files in S3. With key as "Content-Disposition" and value as 'attachment; filename="~actual file name~"'. You can force the name and trigger download.
This way, you don't have to download any files to local file system.
The caveat to this is if someone else is also requesting the same file with in milli-seconds, first user might get the name as requested by second user.
use the rename() method
example:
$file = "boo.png";
$newName = "scary";
$ext = substr( $file, strpos( "." ), strlen( $file ) );
$path = "/images/downloaded/";
rename($path . $file, $path . $newName . $ext);
php documentation

Saving a PDF file using PHP

I have a problem with saving PDF files to folders on my server. The code worked at one time and now it doesn't. What I want it to do is to check if someone is trying to upload a PDF when a form is submitted, and if there is a PDF in the file field it uploads it and then saves the path to the mysql database. Code is below:
if (!empty($_FILES['pdf'])){
$idir = "../files/PDF/"; //my directory file is supposed to be saved in
$randomd=rand(0000000,9999999); //creates a random number as filename
$domain = "http://".$_SERVER['HTTP_HOST'];
$file_ext = strrchr($_FILES['pdf']['name'], '.'); grabs file extension. my code checked if the file was a pdf a different way and neither seems to work.
$destination=$randomd.$file_ext; //new filename
if ($file_ext=='pdf') {
move_uploaded_file($_FILES['pdf']['tmp_name'], "$idir" . $destination);
$pdf= $domain."/files/PDF/".$destination; } else { echo("File type not supported.");
mysql_query("UPDATE tbl_listings SET pdf='$pdf' WHERE listing_id='$lid'");
}
The if not empty does not work and it always tries to upload a file, but when I check the folder nothing is in there and it doesnt update the mysql.
$_FILES['pdf'] will never be empty(when the form has been submitted), no matter if a file has been selected or not, it will always return an array.
Check $_FILES['pdf']['error'] , it will be 4 when no file has been uploaded.

Categories