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;
Related
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;
I have a webservice with a function like this
$app->get('/downloadPdf', function () use($app)
{
$log = 'example.pdf';
$res = $app->response();
$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);
});
Testing it with Advanced Rest Client works fine..
Question is .. how do i call it from my client with all the headers etc.
To specify more. I know there are a lot of examples on how to download a specific file by inserting its url into the curlopt_url with the complete address to the file. What i want is to let the webservice decide which file to return...
Thanks
Never got an answer .. so !!!
This is how i made it work....
Service Function can be seen below
$app->post('/downloadReport', 'authenticate', function() use ($app)
{
verifyRequiredParams(array('reportId'));
$body = $app->request()->getBody();
$params_str = urldecode($body);
$input = json_decode($params_str,true);
$report_id = $input['reportId'];
$db = new DbHandler();
$db->getReport($report_id);
$path = $db->getReportPdfPath($report_id);
$res = $app->response();
$res['Content-Description'] = 'File Transfer';
$res['Content-Type'] = 'application/octet-stream';
$res['Content-Disposition'] ='attachment; filename=' . basename($path);
$res['Content-Transfer-Encoding'] = 'binary';
$res['Expires'] = '0';
$res['Cache-Control'] = 'must-revalidate';
$res['Pragma'] = 'public';
$res['Content-Length'] = filesize($path);
readfile($path);
});
Called the function like this:
public function downloadReport($api_key,$id)
{
$curl_post_data = array('reportId' => $id);
$headers = array('Content-type: application/json','Authorization: '.$api_key,);
$fp = fopen (dirname(__FILE__) . '/localfile.tmp', 'w+');//This is the file where we save the information
$curl = curl_init(DONWLOAD_REPORT);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($curl_post_data));
curl_setopt($curl, CURLOPT_USERPWD, $api_key);
$file = curl_exec($curl);
if ($file === false)
{
$info = curl_getinfo($curl);
curl_close($curl);
die('error occured during curl exec. Additioanl info: ' . var_export($info));
}
curl_close($curl);
header('Content-type: ' . 'application/octet-stream');
header('Content-Disposition: ' . 'attachment; filename=report.pdf');
echo $file;
}
I have same problem with you,
I am using this code, and it works, return a pdf file from webservice.
$api = new RestClient(array(
'base_url' => 'http://another-webservice.com/',
'headers' => array(
'X-Token' => $res->headers->x_token,
'Accept' => 'application/pdf',
),
));
$result = $api->execute("reports/report",'GET', $params);
$filename = 'Report.pdf';
header('Content-type: application/pdf');
header('Content-Disposition: inline; filename="' . $filename . '"');
header('Content-Transfer-Encoding: binary');
header('Accept-Ranges: bytes');
echo $result->response;
References: How to display PDF in Browser
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:
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;
I'm trying to deliver a CSV file through an action. The mime-type in the response still shows as text/html. Can someone help? Thanks
//$this->setLayout(false);
//$this->getUser()->shutdown();
//sfConfig::set('sf_web_debug', false);
$response = $this->getContext()->getResponse();
$response = $this->getResponse();
$response->clearHttpHeaders();
$response->setHttpheader('Pragma: public', true);
$response->addCacheControlHttpHeader('Cache-Control', 'must-revalidate');
$response->setContentType('application/octet-stream', true);
$response->setHttpHeader('Content-Description', 'File Transfer');
$response->setHttpHeader('Content-Transfer-Encoding', 'binary', true);
$response->setHttpHeader('Content-Length', filesize($file_path));
$response->setHttpHeader('Content-Disposition', 'attachment; filename="' . $file_name . '"');
$this->getResponse()->sendHttpHeaders();
//$response->setContent(file_get_contents($file_path));
//readfile($file_path);
$this->renderText(file_get_contents($file_path));
//return sfView::NONE;
return sfView::HEADER_ONLY;
Trust me, before getting here, I've all search results purple. As you can see all the permutations & combinations above, I still couldn't get this working!
Try something like this:
$path = 'absolute/path/to/the/file';
/** #var $response sfWebResponse */
$response = $this->getResponse();
$response->setContentType('text/csv');
$response->setHttpHeader('Content-Disposition', 'attachment; filename="' . basename($path) . '"');
$response->setContent(file_get_contents($path));
return sfView::NONE;
Note: filename must be ASCII see RFC 6266