I made a script where depending on the request the server will return a specific file for download from an external source, I use an external source because I have unlimited bandwidth on the other server:
$ch = curl_init();
$url="http://www.example.com/downloads/$fileName";
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY, true); // make it a HEAD request
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$head = curl_exec($ch);
$mimeType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
$size = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Type: $mimeType");
header("Content-length: $size");
header("Content-Transfer-Encoding: binary");
header("Content-Disposition:attachment;filename='$fileName'");
readfile($url);
$filename comes from the request on the front end. It is a simple post from a form.
Everything works great, but some users, not all, have reported that they cant open the file because the file name has quotes: 'filename.zip', instead of filename.zip
I hit a wall, I have no idea even where to start looking, apparently this happens with some mac users. Any ideas?
The HTTP standard would have you putting double quotes around the filename parameter in the Content-Disposition header. That might look like this in your code:
header('Cache-Control: public');
header('Content-Description: File Transfer');
header('Content-Type: ' . $mimeType);
header('Content-Type:application/octet-stream');
header('Content-length: ' . $size);
header('Content-Transfer-Encoding: binary');
header('Content-Disposition:attachment;filename="' . $fileName . '"');
readfile($url);
Note here that I have changed all your PHP string definitions to use single quotes to present all string definitions consistently.
Related
We are hosting some heavy files that are advertised as free downloads on one of our sites. These files are on another server so in order to generate the download we execute this code:
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Type:application/octet-stream");
header("Content-Transfer-Encoding: binary");
header("Content-Disposition:attachment;filename='$fileName'");
readfile("http://mysiste.com/downloads/$fileName");
Where $fileName is the name of the zip files. Example: myfile.zip
All works fine except that if myfile.zip is 8Mb on the server it will download as 16Mb! The craziest thing is that the file works fine and when unzipping the file all the files inside are complete and not corrupted.
I guess it has something to do with the headers and the transfer encoding as if the zip file looses the compression.
Any ideas?
I think you are missing out an important header
header("Content-length: $size") here. You can use int filesize (string $filename) to find the file size. Here is the API doc
<?php
$fileName = "downloaded.pdf"
$size = filesize($file);
header('Content-type: application/pdf');
header("Content-length: $size");
header("Content-Disposition: attachment; filename='$fileName'");
readfile($file);
?>
If the file is located in a remote server, you can GET the Content-length easily by setting up the Curl without actually downloading it.
These stackoverflow threads should help you:
Easiest way to grab filesize of remote file in PHP?
PHP: Remote file size without downloading file
Reference credit: Content-length and other HTTP headers?
This is the code that would combine curl and PHP headers:
$url="http://mysite/downloads/$fileName";
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY, true); // make it a HEAD request
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$head = curl_exec($ch);
$mimeType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
$size = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
$path = parse_url($url, PHP_URL_PATH);
$filename = substr($url, strrpos($path, '/') + 1);
curl_close($ch);
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Type:$mimeType");
header("Content-Disposition:attachment;filename='$fileName'");
header('Content-Length: '.$size);
readfile($url);
I hope this helps!
I created login using cURL to this site "https://svn.logicgenie.com:8090/svn/" and displayed only file names in it. Now i need to download particular file from SVN. How can i do that? Please help me with the codes.
I used this cURL Script:
$url = "https://svn.logicgenie.com:8090/svn/cheapssls/trunk/admin.php";
set_time_limit(0);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_USERPWD, "firstuser:welcome");
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
$r = curl_exec($ch);
echo $r;
curl_close($ch);
header('Expires: 0'); // no cache
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
header('Cache-Control: private', false);
header('Content-Type: application/force-download');
header('Content-Disposition: attachment; filename="' . basename($url) . '"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . strlen($r)); // provide file size
header('Connection: close');
The url used, to download file is "https://svn.logicgenie.com:8090/svn/cheapssls/trunk/admin.php". Only the file name "admin.php" is getting open. It is not working with some other file name or link . What would be the problem? Please do help me.
If you want to download a file from SVN repository you just have to checkout the file from the repository. By default, you wont have a directory tree on the remote repository, on the svn repository there aren't any /trunk/path/to/something, so you have to checkout in order to obtain some file.
$svn co http://path/to/your/file nameForYourFile
This command will get your file. If you want to obtain your file without history or SVN files, just make:
$svn export http://path/to/your/file nameForYourFile
Hope it helps!
I want to get a video file from another server, and open it on my website in flowplayer.
I did this :
$ch = curl_init('http://example.com/file.flv');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
header("Content-Type: video/flv");
header("Content-Disposition: attachment; filename=video.flv;" );
header("Content-Length: ".$size);
readfile($result);
But there is a problem with the size of file, it always shows wrong size of file (too small)
I have to use a cURL, because only my server IP can get a video from video server.
Thanks for help
readfile() is used to reads a file and write it to the output, use echo instead of, otherwise, use strlen to get content length, try the code below:
$ch = curl_init('http://example.com/file.flv');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
$size = strlen($result);
header("Content-Type: video/flv");
header("Content-Disposition: attachment; filename=video.flv;" );
header("Content-Length: ".$size);
echo $result;
I had a code that worked, here:
if (isset($_GET['file']) && isset($_GET['name']))
{
$file = base64_decode($_REQUEST['file']);
$ch = curl_init($file);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
$data = curl_exec($ch);
curl_close($ch);
if (preg_match('/Content-Length: (\d+)/', $data, $matches))
{
// Contains file size in bytes
$contentLength = (int)$matches[1];
}
header("Content-type: application/x-file-to-save");
header("Content-Disposition: attachment; filename=".$_REQUEST['name']);
header('Content-Length: ' . $contentLength);
header('Content-Transfer-Encoding: binary');
#readfile($file);
}
And than I changed my server to a differenf host providor, and the code stoped working. When I download from chrome it just keeps loading and loading,
but when I download from a download manager like IDM than it gets the correct name and size, but when I press start it makes an error that says something about not supporting resuming or something...
Again, the code worked when I used a different providor.
It might be errors which are outputted before readfile() use ob_clean();
flush(); to remove any output before readfile
I have this code:
if (isset($_GET['file']) && isset($_GET['name']))
{
$ch = curl_init($file);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
$data = curl_exec($ch);
curl_close($ch);
if (preg_match('/Content-Length: (\d+)/', $data, $matches))
{
// Contains file size in bytes
$contentLength = (int)$matches[1];
}
header("Content-type: application/x-file-to-save");
header("Content-Disposition: attachment; filename=".$_REQUEST['name']);
header('Content-Length: ' . $contentLength);
header('Content-Transfer-Encoding: binary');
header('Accept-Ranges: bytes');
header('Connection: Keep-Alive');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0', false);
header('Cache-Control: private', false);
readfile($file);
}
The problem is that $file is located on a different server.
How do I enable resuming this download?
Thanks!
Modify your curl to also pull in the body (remove the NOBODY option)
You can then split the returned content ($data) into the header and the body - the split is the first blank line (look for two carriage returns in a row - the stuff above this is the header, anything below is the body). Put them into variables $header and $body.
Run your preg_match on $header.
To output the rest, simply "echo $body", or substr($body, $startpos) for a resumable download.
Resumable downloads: if the file is large, then you might want to cache it locally. (i.e. save "$body" to a local file, and then use readfile on the local file. Then you can handle the resumable download by opening the file (fopen) and going to the right place (fseek) and reading (fread) / echoing (echo) the rest from there. fpassthru should do the same too, once you've found the starting point.
Also, if you're trying to pass off someone else's content as your own, get permission first ;) (otherwise why not simply direct the user to that other URL and save yourself the bandwidth in and out)