Downloaded file via zend rest is corrupted - php

I want to download a zip file via zend framework. For this purpose I built the following code snippet:
function fetchAll($params = []) {
$file = __DIR__ . '/../../../../htdocs/_downloads/test.zip';
$response = new Stream();
$response->setStream(fopen($file, 'rb'));
$response->setStatusCode(200);
$response->setStreamName(basename($file));
$headers = new Headers();
$headers->addHeaders([
'Content-Disposition' => 'attachment; filename="' . basename($file) . '"',
'Content-Type' => 'application/octet-stream',
'Content-Length' => filesize($file)
]);
$response->setHeaders($headers);
return $response;
}
The code above is part of a rest resource.
Now I downloaded the file twice: Once with WinSCP, once with my REST API. The files I got have exactly the same size on my disk (1,390,687 bytes). The problem is that I can open the zip file downloaded by WinSCP but cannot open the file downloaded via REST. The error message says that the file is corrupted.

Related

Downloading a zip file from a folder on my web server

I have an application that allows users to download mulitple files from a S3 bucket through an EC2 instance in one zip folder.
The process works fine, however, I would like the system to create a folder for each user when they want to download files, and then the files are downloaded to this folder and then put in a zip folder, within the user's folder, and then this zip folder is downloaded.
The files are downloading to this folder fine, and they are also zipping correctly, but my problem is that it is not downloading the zip file from the user's folder, and the download is showing as an empty zip file.
Below is my 'download all' code:
mkdir($userId);
$allName = $vshort . "v" . $number . "_All.zip";
$allName = str_replace(" ", "_", $allName);
if (isset($_POST['downloadAll']))
{
$zip = new ZipArchive();
$zip->open("{$userId}/{$allName}", ZipArchive::CREATE);
$s3->registerStreamWrapper();
foreach ($items as $item)
{
$fid = $item->item_file_id;
$file = ItemFile::locateId($fid);
$a = $file[0]->item_file_type;
$b = $file[0]->item_file_app;
$c = $file[0]->item_file_name;
$fileName = $a . "/" . $b . "/" . $c;
$download = $s3->getCommand('GetObject', [
'Bucket' => AWS_BUCKET,
'Key' => $fileName
]);
$streamFile = $c;
$user = User::locate($_SESSION['email']);
$uid = $user->user_id;
$cid = $user->user_company_id;
$history = new History();
$history->insert($fid, $uid, $cid);
$req = $s3->createPresignedRequest($download, '+1 minutes');
$link = (string)$req->getUri();
$stream = file_get_contents($link);
header($_SERVER["SERVER_PROTOCOL"] . " 200 OK");
header("Cache-Control: public"); // needed for internet explorer
header("Content-Type: application/'$a'");
header("Content-Disposition: attachment; filename='$c'");
file_put_contents($streamFile, $stream);
$zip->addFile($streamFile);
$downArray[] = $streamFile;
}
$zip->close();
$dLink = "https://mywebsite/" . $userId . "/" . $allName;
$size = filesize($allName);
header("Content-type: application/zip");
header("Content-Disposition: attachment; filename=$allName");
header("Content-length: " . $size);
header("Expires: 0");
ob_end_clean();
stream_context_set_default([
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
]
]);
readfile($dLink);
unlink($allName);
foreach ($downArray as $down)
{
unlink($down);
}
}
I have a feeling it is something to do with my headers but I'm not sure, any help will be greatly appreciated.
NOTE: I will be deleting the folder once the user has exited the page.
Below is what I am seeing on the web server
The folder '6' is the user id for the user in question
You appear to be calling readfile() on your download link ($dLink), which would serve the file throught HTTP. You probably meant to call readfile on $allName, the filesystem path of the same file. My guess is that your http request to $dLink is failing somehow. Also, you mkdir($userId), but never use that directory.
Apart from that, it seems your $allName variable contains the basename of the zipfile (i.e 'myzip-v-20_ALL.zip'). I would recommend opening your zipfile to a full path to your data directory, so you keep filesystem and http paths separate.
$allNameFilesystem = "{$userId}/{$allName}";
$allNameHttp = "https://mywebsite/{$userId}{$allName}";
// ...
$zip->open($allNameFilesystem, ZipArchive::CREATE);
// ...
// The download link is $allNameHttp
That is, if you actually need the http path. In this case, if you change readfile to use the filesystem path, you really don't use the http path anymore in your code.

Laravel download file from S3 route (not open in browser)

I have the following route that will load a file from the given URL, I need this to actually download the file (mp4, jpg, pdf) rather than open in the browsers in built viewer.
// Download from CDN Route
Route::get('cdn/{url}', function($url)
{
return Redirect::away($url);
})->where('url', '(.*)');
All files are stored externally so apparently Resource::download() wouldn't actually work.
All I have available to me is the Amazon URL: https://mybucket.s3.amazonaws.com/folder/filename.pdf
Any suggestions on how to force the browser to download the file from S3?
In my case simple anchor link is downloading the file ...
file name
Try this code.
Update composer with flysystem
$path = 'https://mybucket.s3.amazonaws.com/folder/filename.pdf';
if(Storage::disk('s3')->has($path)){
$data = Storage::disk('s3')->get($path);
$getMimeType = Storage::disk('s3')->getMimetype($path);
$newFileName = 'filename.pdf';
$headers = [
'Content-type' => $getMimeType,
'Content-Disposition'=>sprintf('attachment; filename="%s"', $newFileName)
];
return Response::make($data, 200, $headers);
}
pari answer download empty file.
$s3Client = AWS::createClient('s3');
$stream = $s3Client->getObject(
[
'Bucket' => 'bucket name',
'Key' => 'filename',
'SaveAs' => '/tmp'.filename
]);
return response($stream['Body'], 200)->withHeaders([
'Content-Type' => $stream['ContentType'],
'Content-Length' => $stream['ContentLength'],
'Content-Disposition' => 'attachment; filename="' .{filename with extention} . '"'
]);
How can we download a video with an external link from another server. It's not s3. It's a normal server, files are there, but it doesn't work. I have written this code
$filename = $video->video_id;
$tempFile = tempnam(sys_get_temp_dir(), $filename);
// dd($tempFile);
copy($video->path, $tempFile);
header("Content-Disposition: attachment; filename = ".$filename);
header("X-Accel-Redirect: ".$filename);
return response()->download($tempFile, $filename);

Laravel 5.1 - how to download pdf file from S3 bucket

I am using Laravel's Storage facade and I am able to upload the pdf to S3 and I am also able to get() its contents but I cannot display or download it to the end user as an actual pdf file. It just looks like raw data. Here is the code:
$file = Storage::disk($storageLocation)->get($urlToPDF);
header("Content-type: application/pdf");
header("Content-Disposition: attachment; filename='file.pdf'");
echo $file;
How can this be done? I have checked several articles (and SO) and none of them have worked for me.
I think something like this will do the job in L5.2:
public function download($path)
{
$fs = Storage::getDriver();
$stream = $fs->readStream($path);
return \Response::stream(function() use($stream) {
fpassthru($stream);
}, 200, [
"Content-Type" => $fs->getMimetype($path),
"Content-Length" => $fs->getSize($path),
"Content-disposition" => "attachment; filename=\"" .basename($path) . "\"",
]);
}
you can create a download url, using the getObjectUrl method
somthing like this:
$downloadUrl = $s3->getObjectUrl($bucketname, $file, '+5 minutes', array(
'ResponseContentDisposition' => 'attachment; filename=$file,'Content-Type' => 'application/octet-stream',
));
and pass that url to the user. that will direct the user to an amzon page which will start the file download (the link will be valid for 5 minutes - but you can change that)
another option, is first saving that file to your server, and then let the user download the file from your server
You can do with this code (replace with your directory and your file name) ....
Storage::disk('s3')->download('bucket-directory/filename');
If your bucket is private, this is the way to obtain a url to download the file.
$disk = \Storage::disk('s3');
if ($disk->exists($file)) {
$command = $disk->getDriver()->getAdapter()->getClient()->getCommand('GetObject', [
'Bucket' => \Config::get('filesystems.disks.s3.bucket'),
'Key' => $file,
'ResponseContentDisposition' => 'attachment;'
]);
$request = $disk->getDriver()->getAdapter()->getClient()->createPresignedRequest($command, '+5 minutes');
$url = (string)$request->getUri();
return response()->json([
'status' => 'success',
'url' => $url
]);
}
In Laravel 5.7 it can be done with streamDownload:
return response()->streamDownload(function() use ($attachment) {
echo Storage::get($attachment->path);
}, $attachment->name);
$filename = 'test.pdf';
$filePath = storage_path($filename);
$header = [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'inline; filename="'.$filename.'"'
];
return Response::make(file_get_contents($filePath), 200, $header);
$d = file_full_path_here...
$d = str_replace(' ', '%20', $d); //remove the white space in url
ob_end_clean();
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=" . $d);
header("Content-Type: application/pdf");
return readfile($d);
I figured it out. Silly mistake. I had to remove the single quotes from filename.
Fix:
$file = Storage::disk($storageLocation)->get($urlToPDF);
header("Content-type: application/pdf");
header("Content-Disposition: attachment; filename=file.pdf");
echo $file;

Issues when downloading file from Slim Framework

I want to download file through Slim Framework (The reason why I'm using Slim Framework is because I want to write a simple REST API). I found this post:
Download file from Slim Framework 2.4 and this post: http://help.slimframework.com/discussions/questions/359-file-download. I followed the method. Here is my code:
$app->get('/download/:id', 'authenticate', function($id) use ($app)
{
$log = '../download/'.$id.'/myfile.zip';
$res = $app->response()->headers();
$res['Content-Description'] = 'File Transfer';
$res['Content-Type'] = 'application/octet-stream';
$res['Content-Disposition'] ='attachment; filename=' . basename($log);
$res['Content-Transfer-Encoding'] = 'binary';
$res['Expires'] = '0';
$res['Cache-Control'] = 'must-revalidate';
$res['Pragma'] = 'public';
$res['Content-Length'] = filesize($log);
readfile($log);
// NOTE:
// $response = array();
// $response["error"] = false;
// echoRespnse(200, $response);
});
I'm using the Google Chrome's Advanced Rest Client application to test. The problem is the browser hung after the file downloaded.If I comments out the NOTE part in my source code, the browser won't hang again. But I got a "Unexpected token P" error.
Can anybody help? Thanks.
Try the solution here: PHP readfile() adding extra bytes to downloaded file.
You lack the calls to ob_clean, flush and exit.
The problem might be becasue of extra characters output with the rest of the file contents.
For Slim 3, I had a bit of struggle.
I struggled with this in Slim 3.0.
I was trying something like the following and the pdf binary was getting displayed on the screen.
$header['Content-Description'] = 'File Transfer';
$header['Content-Disposition'] = 'attachment; filename="' .basename("$file") . '"';
// ...
$response = $response->withHeader($header);
In order to make it work, you need to set the key/value pairs using $response->withHeader(). And it works like a charm. For example:
$response = $response->withHeader('Content-Type', 'application/pdf');
$response = $response->withHeader('Content-Description', 'File Transfer');
$response = $response->withHeader('Content-Disposition', 'attachment; filename="' .basename("$file") . '"');
$response = $response->withHeader('Content-Transfer-Encoding', 'binary');
$response = $response->withHeader('Expires', '0');
$response = $response->withHeader('Cache-Control', 'must-revalidate');
$response = $response->withHeader('Pragma', 'public');
$response = $response->withHeader('Content-Length', filesize($file));
maybe it's a little late but you can use this package for slim http file response :
https://github.com/mhndev/slim-file-response
Is the solution above working for you? Here's my code, which just outputs obscure characters directly into the browser window:
$app->get('/file/:fileid', function($fileid) use ($app)
{
$zip = new ZipArchive();
$zipname = "zipfile3.zip"; // Zip name
$zip->open($zipname, ZipArchive::CREATE);
$files = array ( "sprecher_inserts_en_VB.doc" );
foreach ($files as $file) {
$path = "uploads/".$file;
if(file_exists($path)){
$zip->addFromString(basename($path), file_get_contents($path));
}
else{
echo"file does not exist";
}
}
$zip->close();
// after that, the zip file is on the server and valid when downloaded manually (e.g. by entering its URL directly)
$res = $app->response()->headers();
$res['Content-Description'] = 'File Transfer';
$res['Content-Type'] = 'application/octet-stream';
$res['Content-Disposition'] ='attachment; filename=' . basename($zipname);
$res['Content-Transfer-Encoding'] = 'binary';
$res['Expires'] = '0';
$res['Cache-Control'] = 'must-revalidate';
$res['Pragma'] = 'public';
$res['Content-Length'] = filesize($zipname);
ob_clean();
flush();
readfile($zipname);
exit();
});
The output in the browser window is the following, no dialog for downloading the file is loaded whatsoever:

Why Response::download() won't download anything except PDF in Laravel 4?

This is the line of code which is giving me sick.
return Response::download(storage_path().'/file/' . $file->id . "." . $file->file->extension);
The files are uploaded and given an id which they are saved under e.g. 25.pdf this works fine if the file is a PDF but doesn't for anything else e.g. PNG. we upgraded from Laravel 3 to 4 to try to overcome this problem.
Any ideas?
EDIT:
I just uploaded a test text file with the word test in it once I uploaded it and then downloaded it I opened it, there were 3 blank lines and the letters te!!!!!I downloaded it through sftp and the file is correctly stored on the server so it is defiantly the download procedure!
I used this function instead of any of the Laravel stuff. :/
(Stolen from other places around the web)
public static function big_download($path, $name = null, array $headers = array()) {
if (is_null($name))
$name = basename($path);
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$pathParts = pathinfo($path);
// Prepare the headers
$headers = array_merge(array(
'Content-Description' => 'File Transfer',
'Content-Type' => finfo_file($finfo, $path),
'Content-Transfer-Encoding' => 'binary',
'Expires' => 0,
'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
'Pragma' => 'public',
'Content-Length' => File::size($path),
'Content-Disposition' => 'inline; filename="' . $name . '.' . $pathParts['extension'] . '"'
), $headers);
finfo_close($finfo);
$response = new Symfony\Component\HttpFoundation\Response('', 200, $headers);
// If there's a session we should save it now
if (Config::get('session.driver') !== '') {
Session::save();
}
// Below is from http://uk1.php.net/manual/en/function.fpassthru.php comments
session_write_close();
ob_end_clean();
$response->sendHeaders();
if ($file = fopen($path, 'rb')) {
while (!feof($file) and (connection_status() == 0)) {
print(fread($file, 1024 * 8));
flush();
}
fclose($file);
}
// Finish off, like Laravel would
Event::fire('laravel.done', array($response));
$response->foundation->finish();
exit;
}
One may ask, How can i get path to file in laravel?
Path to file can be achieved like:
public function getDownload(){
$file = public_path()."/downloads/info.pdf";
$headers = array('Content-Type: application/pdf',);
return Response::download($file, 'info.pdf',$headers);
}
function will download file from : 'project/public/download' folder.
(don't forget to set-up routes and controller by your self)
Try including the MIME in the return:
$file = storage_path().'/file/' . $file->id . "." . $file->file->extension;
return Response::download($file, 200, array('content-type' => 'image/png'));
If you are using Windows, go to php.ini and then uncomment "extension=php_fileinfo.dll" section and then use this code:
Route::get('file/download', function()
{
$file = public_path(). '\download\myfile.png';
return Response::download($file);
});

Categories