PHP - Returning response with downloadable url - php

I am trying for hours to figure out how to return my csv file trough URL in response.
The csv file is there, url is matched but I don't know how can I return downloadable url in my response.
$rootDir = $this->container->get('kernel')->getRootDir();
$dir = $rootDir . '/../web/uploads/news/';
/// - rest of the code that is not relevant and generates my csv $fileName
$fp = fopen($fileName, 'w');
In this part I am trying to set my response headers and downloadable url with no luck:
$baseurl = $request->getScheme() . '://' . $request->getHttpHost()
. $request->getBasePath() . $dir . $fileName;
// Set response
$response = new BinaryFileResponse($dir .DIRECTORY_SEPARATOR. $fileName);
// Set headers
$d = $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
$fileName
);
$response->headers->set('Content-Disposition', $d);
return $this->success();

$response = new BinaryFileResponse($pathToFile);
$response->setContentDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
'filenameThatWillBeDownloaded.yourextension'
);
return $response;
dont forget to import the classes
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;

Related

slim create response with guzzle streamFor content

I have an issue when building a slim (slim 4) response with a streamed content (a huge xlsx file content).
$fileHandler = \fopen(getenv('SHARED_FILES_PATH') . $filename, 'r');
if (!$fileHandler) {
throw new \Exception('Unable to open file ' . getenv('SHARED_FILES_PATH') . $filename . ' for reading.');
}
$stream = GuzzleHttp\Psr7\Utils::streamFor($fileHandler);
// $this->response is Psr\Http\Message\ResponseInterface
$response = $this->response;
$response = $response->withHeader('Content-Disposition', 'attachment; filename="' . $filename . '"');
$response = $response->withHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
$response = $response->withBody($stream);
return $response;
The error:
Fatal error: Uncaught RuntimeException: Unable to read from stream in /home/vincent/workspace/opco2i_applis/opco2i_portail/server/vendor/guzzlehttp/psr7/src/Stream.php on line 232
I have made some check and the file exists and is readable.
If i do the following after the fopen call:
$contents = '';
while (!feof($fileHandler)) {
$contents .= fread($fileHandler, 8192);
break;
}
fclose($fileHandler);
var_dump($contents);die();
, i can read some content of my file.
Can you help me to find why guzzle stream cannot work in this case ?
After some tests, i have found the solution.
I must use $response->getBody()->write($stream) instead of $response->withBody($stream);
$stream = GuzzleHttp\Psr7\Utils::streamFor($fileHandler);
// $this->response is Psr\Http\Message\ResponseInterface
$response = $this->response;
$response = $response->withHeader('Content-Disposition', 'attachment; filename="' . $filename . '"');
$response = $response->withHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
$response = $response->getBody()->write($stream);
return $response;

Can i display/open/preview base64 pdf without store in Laravel

Currently I am storing file into storage folder then show that file into browser and when close that file then remove that file from storage but I want directly show into browser and also can be download without store. This is working but storing file.
$fileFromAPI = $apiResponse->file;
$bin = base64_decode($fileFromAPI, true);
$path = config('pdf_paths.file') . $id . "/";
if (!is_dir($path)) {
$oldmask = umask(0);
Storage::makeDirectory($path, 0777, true);
umask($oldmask);
}
Storage::put($path . 'file_' . $fid . '.pdf', $bin);
$pdfread = 'storage/' . $path . 'file_' . $fid . '.pdf';
return response()->file($pdfread)->deleteFileAfterSend(true);
I found solution just set header and return it.
$fileFromAPI = $apiResponse->file;
$bin = base64_decode($fileFromAPI, true);
return response($bin)
->header('Content-Type', 'application/pdf');

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:

Symfony2 create and download zip file

I have one application that upload some files and then I can compress as zip file and download.
The export action:
public function exportAction() {
$files = array();
$em = $this->getDoctrine()->getManager();
$doc = $em->getRepository('AdminDocumentBundle:Document')->findAll();
foreach ($_POST as $p) {
foreach ($doc as $d) {
if ($d->getId() == $p) {
array_push($files, "../web/".$d->getWebPath());
}
}
}
$zip = new \ZipArchive();
$zipName = 'Documents-'.time().".zip";
$zip->open($zipName, \ZipArchive::CREATE);
foreach ($files as $f) {
$zip->addFromString(basename($f), file_get_contents($f));
}
$response = new Response();
$response->setContent(readfile("../web/".$zipName));
$response->headers->set('Content-Type', 'application/zip');
$response->header('Content-disposition: attachment; filename=../web/"'.$zipName.'"');
$response->header('Content-Length: ' . filesize("../web/" . $zipName));
$response->readfile("../web/" . $zipName);
return $response;
}
everything is ok until the line header.
and everytime I'm going here I got the error: "Warning: readfile(../web/Documents-1385648213.zip): failed to open stream: No such file or directory"
What is wrong?
and why when I upload the files, this files have root permissions, and the same happens for the zip file that I create.
SYMFONY 3 - 4 example :
use Symfony\Component\HttpFoundation\Response;
/**
* Create and download some zip documents.
*
* #param array $documents
* #return Symfony\Component\HttpFoundation\Response
*/
public function zipDownloadDocumentsAction(array $documents)
{
$files = [];
$em = $this->getDoctrine()->getManager();
foreach ($documents as $document) {
array_push($files, '../web/' . $document->getWebPath());
}
// Create new Zip Archive.
$zip = new \ZipArchive();
// The name of the Zip documents.
$zipName = 'Documents.zip';
$zip->open($zipName, \ZipArchive::CREATE);
foreach ($files as $file) {
$zip->addFromString(basename($file), file_get_contents($file));
}
$zip->close();
$response = new Response(file_get_contents($zipName));
$response->headers->set('Content-Type', 'application/zip');
$response->headers->set('Content-Disposition', 'attachment;filename="' . $zipName . '"');
$response->headers->set('Content-length', filesize($zipName));
#unlink($zipName);
return $response;
}
solved:
$zip->close();
header('Content-Type', 'application/zip');
header('Content-disposition: attachment; filename="' . $zipName . '"');
header('Content-Length: ' . filesize($zipName));
readfile($zipName);
apparently closing the file is important ;)
Since Symfony 3.2+ can use file helper to let file download in browser:
public function someAction()
{
// create zip file
$zip = ...;
$this->file($zip);
}
ZipArchive creates the zip file into the root directory of your website if only a name is indicated into open function like $zip->open("document.zip", ZipArchive::CREATE). Specify the path into this function like $zip->open("my/path/document.zip", ZipArchive::CREATE). Do not forget delete this file with unlink() (see doc).
Here you have an example in Symfony 4 (may work on earlier version):
use Symfony\Component\HttpFoundation\Response;
use \ZipArchive;
public function exportAction()
{
// Do your stuff with $files
$zip = new ZipArchive();
$zip_name = "../web/zipFileName.zip"; // Users should not have access to the web folder (it is for temporary files)
// Create a zip file in tmp/zipFileName.zip (overwrite if exists)
if ($zip->open($zip_name, ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) {
// Add your files into zip
foreach ($files as $f) {
$zip->addFromString(basename($f), file_get_contents($f));
}
$zip->close();
$response = new Response(
file_get_contents($zip_name),
Response::HTTP_OK,
['Content-Type' => 'application/zip',
'Content-Disposition' => 'attachment; filename="' . basename($zip_name) . '"',
'Content-Length' => filesize($zip_name)]);
unlink($zip_name); // Delete file
return $response;
} else {
// Throw an exception or manage the error
}
}
You may need to add "ext-zip": "*" into your Composer file to use ZipArchive and extension=zip.so in your php.ini.
Answser inspired by Create a Response object with zip file in Symfony.
I think its better that you use
$zipFilesIds = $request->request->get('zipFiles')
foreach($zipFilesIds as $zipFilesId){
//your vérification here
}
with the post variable of your id of zip = 'zipFiles'. Its better of fetching all $_POST variables.
To complete vincent response, just add this right before returning response :
...
$response->headers->set('Content-length', filesize($zipName));
unlink($zipName);
return $response;
Work for me. Where $archive_file_name = 'your_path_to_file_from_root/filename.zip'.
$zip = new \ZipArchive();
if ($zip->open($archive_file_name, \ZIPARCHIVE::CREATE | \ZIPARCHIVE::OVERWRITE) === TRUE) {
foreach ($files_data as $file_data) {
$fileUri = \Drupal::service('file_system')->realpath($file_data['file_url']);
$filename = $file_data['folder'] . $file_data['filename'];
$zip->addFile($fileUri, $filename);
}
$zip->close();
}
$response = new Response();
$response->headers->set('Cache-Control', 'private');
$response->headers->set('Content-type', 'application/zip');
$response->headers->set('Content-Disposition', 'attachment; filename="' . basename($archive_file_name) . '"');
$response->headers->set('Content-length', filesize($archive_file_name));
// Send headers before outputting anything.
$response->sendHeaders();
$response->setContent(readfile($archive_file_name));
return $response;

fopen on url and bytestream

i have a question about fopen() and base64 comunication.
The scenario is that: i have a service A that must fetch from url a resource (png/jpeg or pdf). The code is that:
$uri = urldecode($_POST['uri']);
$imgfile = $uri;
$handle = fopen($uri, 'r');
$imagebinary = '';
while (!feof($handle)) {
$c = fgetc($handle);
if($c === false) break;
$imagebinary .= $c;
}
fclose($handle);
$return = base64_encode($imagebinary);
Now i have JQUERY function that send this $return (something like that: 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAAXNSR0IArs') to another PHP service, named B.
B service take this string and try to save it on disk. In the specific the service B try to save the file on amazon s3, the code is that:
// in $imagedata is saved the string generated by service A
$imagedata = $_POST['serviceA_base64encodedfile'];
// $contentType taken from switch function on $ext
// for example 'image/png'
$filename = sha1(uniqid()) . '.' . $ext;
$full_filename = $path . '/' . $filename;
$stream = fopen('data://' . $contentType . ';base64,' . $imagedata, 'r');
fseek($stream, 0);
$opt = array(
'fileUpload' => $stream,
'acl' => AmazonS3::ACL_PUBLIC,
'contentType' => $contentType
);
$s3 = new AmazonS3(AWS_KEY, AWS_SECRET_KEY);
$response = $s3->create_object($bucket, $filename, $opt);
But the image that being saved is corrupted, in additionals this images or pdf have less bytes then orginal.
I need realy help :D
I'm not 100% sure this will work, but why not base64_decode the data back to binary, then write the data to a temporary file and send it to amazon from that location. Something like (untested):
// in $imagedata is saved the string generated by service A
$imagedata = base64_decode($_POST['serviceA_base64encodedfile']);
if (!$imagedata){
//Handle invalid base64 encoded data
}
// $contentType taken from switch function on $ext
// for example 'image/png'
$filename = sha1(uniqid()) . '.' . $ext;
$full_filename = $path . '/' . $filename;
$tmpfname = tempnam("/tmp", "image_to_upload");
$populated = file_put_contents($tmpfname,$imagedata);
if (!$populated){
//handle write failures
}
$opt = array(
'fileUpload' => "/tmp/".$tmpfname,
'acl' => AmazonS3::ACL_PUBLIC,
'contentType' => $contentType
);
$s3 = new AmazonS3(AWS_KEY, AWS_SECRET_KEY);
$response = $s3->create_object($bucket, $full_filename, $opt);
I'm also presuming on the last call, that $full_filename is where you want to store the file on the s3 server... though you can just use $file_name.

Categories