I offload a lot of my sites uploads to RapidShare for storage. I then have a script that when accessed by a user wanting to download a file, downloads it from RapidShare using their API and serves it to the user. Here's the relevant code in the script that serves the file:
// Stream file to user
header('Content-Description: File Transfer');
header('Content-Disposition: attachment; filename=' . $result->name);
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Content-Length: ' . $result->size);
$url = 'https://' . $host . '/cgi-bin/rsapi.cgi?sub=download';
$url .= '&login=login';
$url .= '&password=mypass';
$url .= '&fileid=' . $result->rs_fileid;
$url .= '&filename=' . $result->name;
$handle = fopen($url, 'rb');
if(!$handle) {
throw new Exception('Failed to open file handle');
}
while(!feof($handle)) {
echo fread($handle, 1024*1024);
ob_flush();
flush();
}
fclose($handle);
This works fine for all the files I've tested except for images. When the images are downloaded, they're corrupt. Windows Photo Viewer can't open the image and neither can programs like Photoshop.
Weird thing is, if I download a .exe and run it, everything works fine. It's not corrupt. It only happens with images as far as I can tell. I've also tested it with .pdf's.
To make it weirder, if I look at the filesize of the original image (2,882 bytes) and then the filesize of the image downloaded through this script, they're the same. The filesizes for both are 2,882 bytes. But the image is still corrupt.
What could be the cause of this and the solution? It's not like I'm adding any binary data to the file as it's downloaded. :/
Thanks.
Edit: Forgot to mention, if I download the file straight from RapidShare without going through the script the image is fine and isn't corrupt. So it must be the script at fault here.
Check my answer for a similar problem, I'm pretty sure there will be some notice at the beginning of your files!
Simplify your code, there is no need for the header() stuff and file_put/get_contents is much sleeker:
$url = 'https://' . $host . '/cgi-bin/rsapi.cgi?sub=download';
$url .= '&login=login';
$url .= '&password=mypass';
$url .= '&fileid=' . $result->rs_fileid;
$url .= '&filename=' . $result->name;
file_put_contents($result->name, file_get_contents($url));
When you have memory problems, use mod_xsendfile, easy to use.
Related
I'm at a bit of a loss as to why this folder is not being found. I have a script that, after searching a database to find the $filename of someone's purchase based on a stored random code, should simply return their file. My code looks like this (including the trailing end of the db query):
$stmt_2 -> bind_result($filename);
$stmt_2 -> fetch();
$stmt_2 -> close();
// For .zip files
$filepath='/media-files/Label/' . $filename;
if (headers_sent()) {
echo 'HTTP header already sent';
} else {
if (!is_file($filepath)) {
header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found');
echo 'File not found.';
} else if (!is_readable($filepath)) {
header($_SERVER['SERVER_PROTOCOL'].' 403 Forbidden');
echo 'File not readable.';
} else {
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="' . basename($filepath) . '"');
header('Content-Length: ' . filesize($filepath));
readfile($filepath);
exit;
}
}
When I run this code, I receive "File not found." so !is_file($filepath) is where it is getting tripped up -- However, the path is correct and the zip is definitely there, so I'm not sure what is wrong here.
In terms of debugging, I've tried removing the checks, going directly to the headers and readfile, which returns an empty zip folder. What does work is if I navigate directly to the file by URL...
UPDATE
The file path issue has been fixed, but I am still not able to download the file. In all attempts I get either ERR_INVALID_RESPONSE or if I try to brute force download the file, it returns an empty file. I tried using these headers with no success:
header_remove();
ob_end_clean();
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($filepath));
readfile($filepath);
ob_end_flush();
exit;
They are large audio files, which appears to be causing the issue...
You have two types of pathes:
(a) The path of an URL. You have a web-adress which defines the root of your webpage.
e.g. https://www.stackoverflow.com is the start of the site. If you adress /questions at this site you always have the path https://www.stackoverflow.com/questions
(b) The path of the drive where the webpage is located. It is the filesystem-root.
e.g. /home/httpd/html/MyWebPage/questions
If you try to use /questions in (b) it will fail because you need the whole path.
So, this said you need to know where '/media-files/Label/'.$filename is located. It seems to me that /media-files is not at root-level of your filesystem (b).
Maybe it is at the web-root but this is not enough for your system to find the file. Therefore you need something like this:
'/root/httpd/MyWebPage/media-files/Label/'.$filename
Nico Haase was absolutely correct, this is an issue with misunderstanding of paths. Here is a link to an article that should clear things up:
https://phpdelusions.net/articles/paths
Currently your script is trying to find the file in:
/media-files/Label/file.zip
not:
/var/www/myproject/media-files/Label/file.zip
The linked article should provide you with all the neccesary information.
TLDR;
use:
$filepath=$_SERVER['DOCUMENT_ROOT'].'/media-files/Label/' . $filename;
UPDATE
With the file size issue it might be that PHP runs out of allowed memory when trying to load the whole file. We could try something like:
flush();
$file = fopen($filepath, "r");
while(!feof($file)) {
// send the current file part to the browser
print fread($file, round(10 * 1024));
// flush the content to the browser
flush();
}
fclose($file);
There are some issues with flush() but it's a good shot I think. You can have a read on: https://www.php.net/manual/en/function.flush
Other then that there is always the possibility to split the file into smaller chunks.
I am having a problem with my webpage. I am building a report tool for downloading data as .csv - I have a php skript which aggregates the data and builds a csv from it. The skript is invoked with the exec() command, detailed code is below. The skript itself uses file_put_contents() to generate the file, which is then stored in my /tmp/ folder until its downloaded (I am working in a dockerized environment and our filter rules delete the file at the next request, but I could store the file permanently somewhere else if that would be neccessary). I am then checking if the file is present with file_exists() and proceed to invoke my download function. In Firefox I get the desired result, a file with the correct content of only the csv data.
My main Problem is: When I download the csv in Chrome I get the csv data followed by the html source of my page - so starting with <!doctype html> in the first line after the csv data, then <html lang="de">in the next line of te csv and so on..
Let me show you some code:
In my skript:
private function writeToFile($csv)
{
$fileName = '/path/to/file' '.csv';
echo "\n" . 'Write file to ' . $fileName . "\n";
file_put_contents($fileName, $csv);
}
In my page class:
$filePath = '/path/to/finished/csv/'
exec('php ' . $skriptPath . $skriptParams);
if (file_exists($filePath)) {
$this->downloadCsv($filePath);
} else {
$pageModel->addMessage(
new ErrorMessage('Error Text')
);
}
My download function in the same class:
private function downloadCsv($filePath)
{
header('Content-Description: File Transfer');
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
header('Content-Length: ' . filesize($filePath));
readfile($filePath);
}
The shown above is working in Firefox, but not in Chrome. I already tried to clear the output buffer with ob_clean() or send and disable it with ob_end_flush() but nothing worked for Chrome.
I also tried something like this in my download function:
header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
$fp =fopen($filePath, 'rw');
fpassthru($fp);
fclose($fp);
This produces the same results in Firefox and Chrome - I get the csv data followed by the html sourcecode mixed into the same file.
I am working within a Symfony framework if that could be from help, I saw there are some helper functions for file downloads but I so far I could not use them with success..
Until now my target is only to get the download working in Chrome to have a working mvp which can go into production - it is supposed to be for internal use only, so I don't have to care about IE or some other abominations because our staff is told to use a normal browser... But when someone sees flaws in the general concept feel free to tell me!
Thanks in advance :)
So I managed to get it working, I was on the wrong track with the output buffer, but a simple exit()after my readfile()was enough to stop parts of the html ending up in the csv file.
Code:
private function downloadCsv($filePath)
{
header('Content-Description: File Transfer');
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
header('Content-Length: ' . filesize($filePath));
readfile($filePath);
exit;
}
I have some ebooks on my server, that I want people to be able to download. I uploaded them and when I download them via my ftp tool then everything is perfect. when I use my script for the user to download it, then I get the following error in calibre:
MobiError: Unknown book type: '\x00\x00\x00BOOKM'
my script that handles the output of the file is as follows:
$file_url = ABSPATH . $file['file'];
$basename = $story->post_title . $subtitle . '.' . $_POST['type'];
$filename = basename(mb_ereg_replace("([^\w\s\d\-_~,;\[\]\(\).])", '', $basename));
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-Disposition: attachment; filename=\"" . $filename . "\"");
readfile($file_url);
die();
Then all mobi readers I got say, that there is a problem with the file. I don't know what is going wrong. Also when I open the files in a text editor they are both different type. The one from my php script looks as follows:
the one that works from my ftp tool looks like this:
Anyone who can help me to find what I am doing wrong? The .epub files by the way are no problem with my script.
I solved this problem by outputting the file as follows:
ob_clean();
flush();
readfile($path);
exit;
I'm currently having some problems with forcing a file download on a Wordpress site I'm making. On my localhost it works fine but when I try it to the live server all file downloads are broken. For instance, images are not displaying, video and audio files don't play etc.
This is my code:
$title = $query[0]->post_title;
$mimetype = $query[0]->post_mime_type;
$guid = $query[0]->guid;
$filepath = str_replace(home_url().'/wp-content/uploads/', '', $guid);
$download = ABSPATH.'/wp-content/uploads/' . $filepath;
$filesize = filesize($download);
//Download file
header('Content-Description: File Transfer');
header('Content-Disposition: attachment; filename='.$title);
header('Content-Type: '.$mimetype);
header('Content-Transfer-Encoding: '.$mimetype);
header('Content-Length: '.$filesize);
ob_clean();
flush();
readfile($download);
exit();
If I inspect the response in Chrome it says "Failed to load response data" both in the preview and response tab.
Any ideas? any help is appreciated.
Would add this as a comment but can't. It will be using ABSPATH that's giving you grief. I'd echo out your $download path and make sure it is correct. Look here - ABSPATH or __FILE__?
<?php
ob_clean();
header('Content-Type: image/jpeg');
header('Content-Length: ' . filesize($file));
$file = '111111-11111.jpg';
$handle = fopen($file, "r");
echo file_get_contents($file);
?>
At one point my php webpage stopped displaying all and any images I had successfully uploaded to MySQL database. I wanted to get down to the roots of this problem and see if it could output a .jpg image stored on HDD and all I get is the same output as when I'm trying access this image from database:
The image [...] could not be displayed because it contains errors.
The image location is correct, there are no whitespaces before header, the output buffer has been cleaned. I have spent days on google searching for the answer, but without luck.
I am quite new to PHP so if you can help, please be as descriptive as you can be, so I could understand.
Thank you in advance.
Be sure that the file you are trying to output is in the correct path (from the code, the same as the PHP script) and that you have the read permissions on the file.
$file = '111111-11111.jpg';
$type = 'image/jpeg';
header('Content-Type:'.$type);
header('Content-Length: ' . filesize($file));
readfile($file);