PHP .zip download failes with 'Content-Length' - php

I'm trying to start a download for a zip file from my server. The server I'm using has debian 9.2 as distribution with nginx 1.13.6 (+libressl) with an active ssl cert.
The download works with Edge and Firefox while setting Content-Length to the filesize but in chrome the download files with the message:
Failed network error
$archiveName = $this->m_path . $this->m_file;
ob_start();
header($_SERVER['SERVER_PROTOCOL'].' 200 OK');
header("Content-Type: application/zip");
header("Content-Transfer-Encoding: Binary");
// works now with chrome, but the file is corrupted
// header("Content-Length: ".filesize($this->m_path . $this->m_file));
header('Pragma: no-cache');
header("Content-Disposition: attachment; filename=\"".$archive_name."\"");
ob_flush();
ob_clean();
readfile($this->m_path . $this->m_file);
Without Content-Length, chrome can download the file but it's corrupted.
I appreciate any help from you,
regards

Just add ob_clean() and flush() at the beginning and end respectively. Worked for me and also this header("Content-Transfer-Encoding: Binary"); needs to be removed.

Related

PHP script only partial image download

When a user runs this PHP script it is supposed to just download the specified image file but for some reason, it's only downloading it partially. It starts the download then stalls at 1,083 KB / 1,382 KB. The download stops after a few seconds and when I open the image, the bottom portion is grey/missing. This script worked fine on my old server but the new server it won't. I tried changing PHP settings and version but no luck.
<?php
if (file_exists("test-download-image.jpg")) {
header("Pragma: no-cache");
header('Expires: 0');
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="test-download-image.jpg"');
header('Content-Length: ' . filesize("test-download-image.jpg"));
readfile("test-download-image.jpg");
}
?>

Corrupted file error on .docx files, when downloading them from PHP

I have a weird problem with downloaded .docx files from my webserver.
I get an corrupted file error, when I try to open the downloaded document.
When I try to download the files manually (via FileZilla) I am able to open them without problems.
$targetFile = ASSETS . "/tmp/{$filename}.{$extension}";
ob_clean();
$phpWord->save($targetFile,$format,false);
if(headers_sent())
{
echo 'http header already sent';die();
}
else{
if (!is_file($targetFile)) {
header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found');
echo 'File not found';die();
} else if (!is_readable($targetFile)) {
header($_SERVER['SERVER_PROTOCOL'].' 403 Forbidden');
echo 'File not readable';die();
} else {
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=".$filename.".".$extension."");
header("Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document");
header("Content-Transfer-Encoding: binary");
header('Connection: Keep-Alive');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header("Content-Length: ".filesize($targetFile));
set_time_limit(0);
readfile($targetFile);
exit();
}
}
Also running the code on localhost works and I am able to open files without problems.
One thing I observed was that the downloaded document misses 1 kb in size, when I download it via my code, so I'm assuming some content is missing, but can't seem to find the problem.
I can see that many have similar problems and I tried the solutions suggested for those, but without luck.
Any ideas?

How to Increase Speed of Downloading an MP3

I'm using the following code to force download some mp3 files that are stored on my server. This works fine but it takes over 1 minute to download 1 mp3 file, even for a file that is 2.5MB. Something seems wrong for it take that long. Any ideas what I can do to make this download a lot faster?
$fullfilename=$_GET['file'];
$filename=basename($fullfilename);
header('Content-type: audio/mpeg');
header("Content-Disposition: attachment; filename=\"{$filename}\"");
header("Content-Length: " . filesize($filename));
header('Content-Transfer-Encoding: binary');
ob_clean();
flush();
readfile($fullfilename);
exit;
It depends on the internet connection between the server and your browser. PHP cannot do anything about it.

Download size on Android incorrect for WAV and ZIP files

I've created a simple download script that works flawless on desktop browsers. However when trying to download ZIP or WAV files on Android it seems to show and download only 36% of the file.. Is this a known error or am I doing something completely wrong here?
My function triggered after checks:
function forceDownload($file_to_download,$header){
header($_SERVER['SERVER_PROTOCOL'].' 200 OK');
header("Content-Type: ".$header);
header("Content-Transfer-Encoding: Binary");
header('Cache-Control: must-revalidate');
header("Pragma: public");
header("Content-Length: ".filesize($file_to_download));
header("Content-Disposition: attachment;
filename=\"".basename($file_to_download)."\"");
ob_end_flush();
#readfile($file_to_download);
exit();
}

Create XML, Json or Text file without saving on the server with PHP [duplicate]

I have a CSV file on my server. If a user clicks on a link it should download, but instead it opens up in my browser window.
My code looks as follows
<a href="files/csv/example/example.csv">
Click here to download an example of the "CSV" file
</a>
It's a normal webserver where I have all of my development work on.
I tried something like:
<a href="files/csv/example/csv.php">
Click here to download an example of the "CSV" file
</a>
Now the contents of my csv.php file:
header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename=example.csv');
header('Pragma: no-cache');
Now my issue is it's downloading, but not my CSV file. It creates a new file.
.htaccess Solution
To brute force all CSV files on your server to download, add in your .htaccess file:
AddType application/octet-stream csv
PHP Solution
header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename=example.csv');
header('Pragma: no-cache');
readfile("/path/to/yourfile.csv");
Or you can do this using HTML5. Simply with
<a href="example.csv" download>download not open it</a>
This cannot be done reliably, since it's up to the browser to decide what to do with an URL it's been asked to retrieve.
You can suggest to the browser that it should offer to "save to disk" right away by sending a Content-disposition header:
header("Content-disposition: attachment");
I'm not sure how well this is supported by various browsers. The alternative is to send a Content-type of application/octet-stream, but that is a hack (you're basically telling the browser "I'm not telling you what kind of file this is" and depending on the fact that most browsers will then offer a download dialog) and allegedly causes problems with Internet Explorer.
Read more about this in the Web Authoring FAQ.
Edit You've already switched to a PHP file to deliver the data - which is necessary to set the Content-disposition header (unless there are some arcane Apache settings that can also do this). Now all that's left to do is for that PHP file to read the contents of the CSV file and print them - the filename=example.csv in the header only suggests to the client browser what name to use for the file, it does not actually fetch the data from the file on the server.
Here is a more browser-safe solution:
$fp = #fopen($yourfile, 'rb');
if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE"))
{
header('Content-Type: "application/octet-stream"');
header('Content-Disposition: attachment; filename="yourname.file"');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header("Content-Transfer-Encoding: binary");
header('Pragma: public');
header("Content-Length: ".filesize($yourfile));
}
else
{
header('Content-Type: "application/octet-stream"');
header('Content-Disposition: attachment; filename="yourname.file"');
header("Content-Transfer-Encoding: binary");
header('Expires: 0');
header('Pragma: no-cache');
header("Content-Length: ".filesize($yourfile));
}
fpassthru($fp);
fclose($fp);
Configure your server to send the file with the media type application/octet-stream.
This means that your browser can handle this file type.
If you don't like it, the easiest method would be offering ZIP files. Everyone can handle ZIP files, and they are downloadable by default.
Nice clean solution:
<?php
header('Content-Type: application/download');
header('Content-Disposition: attachment; filename="example.csv"');
header("Content-Length: " . filesize("example.csv"));
$fp = fopen("example.csv", "r");
fpassthru($fp);
fclose($fp);
?>
A previous answer on this page describes how to use .htaccess to force all files of a certain type to download. However, the solution does not work with all file types across all browsers. This is a more reliable way:
<FilesMatch "\.(?i:csv)$">
ForceType application/octet-stream
Header set Content-Disposition attachment
</FilesMatch>
You might need to flush your browser cache to see this working correctly.
If you are doing it with your application itself... I hope this code helps.
HTML
In href -- you have to add download_file.php along with your URL:
<a class="download" href="'/download_file.php?fileSource='+http://www.google.com/logo_small.png" target="_blank" title="YourTitle">
PHP
/* Here is the Download.php file to force download stuff */
<?php
$fullPath = $_GET['fileSource'];
if($fullPath) {
$fsize = filesize($fullPath);
$path_parts = pathinfo($fullPath);
$ext = strtolower($path_parts["extension"]);
switch ($ext) {
case "pdf":
header("Content-Disposition: attachment; filename=\"" . $path_parts["basename"]."\""); // Use 'attachment' to force a download
header("Content-type: application/pdf"); // Add here more headers for diff. extensions
break;
default;
header("Content-type: application/octet-stream");
header("Content-Disposition: filename=\"" . $path_parts["basename"]."\"");
}
if($fsize) { // Checking if file size exist
header("Content-length: $fsize");
}
readfile($fullPath);
exit;
}
?>
To force download you may use Content-Type: application/octet-stream header, which is supported by most browsers:
function downloadFile($filePath)
{
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
header('Content-Length: ' . filesize($filePath));
readfile($filePath);
}
A BETTER WAY
Downloading files this way is not the best idea especially for large files. PHP will require extra CPU / Memory to read and output file contents and when dealing with large files may reach time / memory limits.
A better way would be to use PHP to authenticate and grant access to a file, and actual file serving should be delegated to a web server using X-SENDFILE method (requires some web server configuration):
X-SENDFILE is natively supported by Lighttpd: https://redmine.lighttpd.net/projects/1/wiki/X-LIGHTTPD-send-file
Apache requires mod_xsendfile module: https://tn123.org/mod_xsendfile/ On Ubuntu may be installed by: apt install libapache2-mod-xsendfile
Nginx has a similar X-Accel-Redirect header: https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/
After configuring web server to handle X-SENDFILE, just replace readfile($filePath) with header('X-SENDFILE: ' . $filePath) and web server will take care of file serving, which will require less resources than using PHP readfile.
(For Nginx use X-Accel-Redirect header instead of X-SENDFILE)
Note: If you end up downloading empty files, it means you didn't configure your web server to handle X-SENDFILE header. Check the links above to see how to correctly configure your web server.

Categories