Soundmanager2, Safari and PHP streaming error - php

I am using soundmanager2 and I have a problem with Safari.
I am successfully streaming protected files (outside the webroot) from PHP to Soundmanager2, using something like this :
//check if user is logged in and has rights on $file
//if yes stream file
if (file_exists($file)) {
$filepath = $file;
$filesize = filesize($filepath);
header("Pragma: no-cache");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header('Content-Disposition: attachment;filename="'.$filename.'"');
header("Content-Transfer-Encoding: binary");
header('Content-Type: audio/mpeg');
header('Content-Length: '.$filesize);
#readfile($filepath);
exit(0);
}
This works fine on Firefox and Chrome, the mp3 files are playing, but in Safari I get :
soundmanager2.js:1190basicMP3Sound0: Using HTML5
soundmanager2.js:1190basicMP3Sound0: play(): Attempting to load
soundmanager2.js:1190basicMP3Sound0: load (/privateaccess/index/1415)
soundmanager2.js:1190basicMP3Sound0: waiting
soundmanager2.js:1190basicMP3Sound0: loadstart
soundmanager2.js:1190basicMP3Sound0: loadedmetadata
soundmanager2.js:1190basicMP3Sound0: HTML5 error, code 3
soundmanager2.js:1188basicMP3Sound0: Failed to load / invalid sound? Zero-length duration reported. (/privateaccess/index/1415)
I only get this error when I stream a file from PHP, it's working with files that are in the webroot (delivered by apache instead of PHP).
If I hit www.myurl.com/privateaccess/index/1415 directly, the file is downloaded, so it really seems like an issue between Safari,Soundmanager2 and PHP file streaming.
Anyone? an idea to fix/workaround this?

You need to support byte range requests. See SoundManager2's Technical Notes on the subject.
Example Request:
GET some.ogg HTTP/1.1
Range: bytes=5210604-5275910
Expected Response:
HTTP/1.1 206 Partial Content
Accept-Ranges: bytes
Content-length: 65307
Content-Range: bytes 5210604-5275910/5275911
Content-Type: audio/ogg

Related

Issue with sending large video through PHP

I have searched and exhausted every solution I could find on stack overflow but with no joy.
Basically, I have a folder of images/videos. .htaccess redirects all requests to a .php file that checks a user can view these videos against a paywall, or redirects them to login. If the user is authorised then it sends the file. This works perctly for images and small videos.
However, once the video gets up to 500mb - 1GB I start getting: "The media could not be loaded, either because the server or network failed or because the format is not supported." On playing said video. Wierder still this seems only to be on safari or chrome.
Firefox plays these videos without any issue what so ever.
Here is my code to output the video:
$mime = mime_content_type($_SERVER['DOCUMENT_ROOT'].'/uploadvideos/'.$video);
ob_clean();
ob_flush();
flush();
header('Expires: 0');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: pre-check=0, post-check=0, max-age=0');
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Length: ".filesize(trim($_SERVER['DOCUMENT_ROOT'].'/uploadvideos/'.$video)));
// Force the download
header("Content-Type: ".$mime);
header('Content-Length: '.filesize($_SERVER['DOCUMENT_ROOT'].'/uploadvideos/'.$video));
readfile($_SERVER['DOCUMENT_ROOT'].'/uploadvideos/'.$video);
Can anyone see any headers that might be missing that I need to add or something to get this working properly, as it is literally driving me insane and I just cant fathom out what the cause is here.
I have added numerous header options but to no joy. I tried this one that some seemed to think from other Stackoverflow posts:
header("Content-Transfer-Encoding: binary");
$size = filesize(trim($_SERVER['DOCUMENT_ROOT'].'/uploadvideos/'.$video));
$begin = 0;
$end = $size - 1;
header("Content-Range: bytes $begin-$end/$size");
But this seemed to make the situation worse, and removing it made it at least work in Firefox.

PHP force download, not sending full binary files

I'm creating a protected file download system for an e-commerce website. I am using PHP to authenticate that a user is logged in and owns the product before it is downloaded. I am using the example provided by the PHP.net manual but have run into an error serving both PDF and PNG files.
It has worked at one point, during development. Now that we've gone live, it seems to have broke... Fortunately the website is not running full-force at the moment so it's not a huge issue right now.
What is happening:
You visit the download URL. You are verified as an owner of the product, and the download starts. Everything appears to be normal, reviewing the headers everything looks OK. The header states the content length is "229542" (224.16kb), which looks OK.
The problem:
When the download completes, the file size is only 222 bytes. There are no errors displayed, and no PHP errors/warnings/notices are being sent in the file or browser. It's as if PHP is being terminated, but without any warnings. Even if I turn debugging on, I don't see any warnings.
The source file looks like this (in Notepad++, it's a PDF so it's binary)
When downloaded, it looks like this (Only 7 lines, compared to 2554)
My guess:
I am fairly certain the issue is header related. Maybe caused by a plugin? This shows up in Chrome's networking console:
Response Headers:
Below are the response headers. The "Connection: close" concerns me. I am not sending that header. I'm guessing it is sent by the exit command.
Access-Control-Allow-Origin:*
Cache-Control:must-revalidate, post-check=0, pre-check=0
Connection:close
Content-Description:File Transfer
Content-Disposition:attachment; filename="workbook.pdf"
Content-Length:229542
Content-Transfer-Encoding:binary
Content-Type:application/pdf
Date:Tue, 03 Sep 2013 21:16:14 GMT
Expires:0
Pragma:public
Server:Apache
X-Powered-By:PleskLin
I have also tried:
Turning on PHP debugging to get some sort of error/warning, but I don't get anything useful in browser or in the downloaded file source.
Outputting to the browser, rather than streaming. The file is still cut short, I would expect a PDF would try to display in the browser's PDF reader - but it doesn't even try. Just a white page with a bunch of unknown symbols.
Using +rb read mode, using fopen( $file['file'], 'rb' ); echo fread( $fh, filesize( $file['file'] ) ); fclose( $fh );
Using ignore_user_abort(true) and set_time_limit(0) to no effect (also tried set_time_limit(10000) just for in case, still nothing)
Sending a .txt file. This worked. I tried with a simple 5-byte text file, and again with an 86.3kb file. Both appear to have come out the same as they went in. So it must be related to binary files...
/* --- This is what the $file variable looks like:
$file = array(
[file] => /var/www/vhosts/my-website/uploads/workbook.pdf
[type] => application/pdf
[filesize] => 229542
[filename] => workbook.pdf
[upload_date] => 1377278303
);
*/
// Stream the file to the user
if (file_exists($file['file'])) {
header_remove(); // Remove any previously set header
header('Content-Description: File Transfer');
header('Content-Type: ' . $file['type']);
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . $filesize);
ob_clean();
flush();
readfile($file['file']);
exit;
}
I know this is an older thread but I have the solution for everyone here.
You have a script somewhere in your application that's compressing spaces or other characters.
ob_start("sanitize_output");
That was the problem in one scenario I noticed. If you turn off the buffer reduction script (called "sanitize_output" above) then your .pdf will show up normally. That extra 2kb's you noticed that were trimmed was probably from spacing or cutting out unneeded characters.
Try this code: Just give $file a relative path to the file and check it.
<?php
// your file to download
$file = path/to/the/file;
header("Expires: 0");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
$ext = pathinfo($file, PATHINFO_EXTENSION);
$basename = pathinfo($file, PATHINFO_BASENAME);
header("Content-type: application/".$ext);
// tell file size
header('Content-length: '.filesize($file));
// set file name
header("Content-Disposition: attachment; filename=\"$basename\"");
readfile($file);
// Exit script. So that no useless data is output-ed.
exit;
?>
Let php calculate the file size and date and other stuff. It is quiet possible that an error can occur while feeding it via an array.
Also in this code I have used pathinfo so as to automatically set the Content-type and file name.
I would like to contribute for someone that was lost with this solution.
What worked for me, has said above by #Kalob Taulien was insert these lines that turn off a compactation algorith builtin that was corrupting my files on download by the script, here is:
#ob_end_clean();
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'Off');
After insert that before fpassthru($file) function, the downloads worked well.

PHP offered download interrupted after X number of KB everytime

I have written a code that offers visitors to download a csv file which is of 1.1M in size. If one visits this script, download is interrupted around 30-40K (as shown below in wget output) while if he downloads it via a direct link like http://domain.com/events.csv it works just fine. I believe this has something to do with php configuration values on the server but i have played almost with all values[relevant and non-relevant] such as
post_max_size [upto 90M]
max_file_upload [upto 90M]
max_execution_time [0 and upto 600]
max_input_time [0 and upto 600]
memory_limit [upto 1024M]
Following contains my code:
<?php
$realpath="/home/user/public_html/events.csv";
$size = intval(sprintf("%u", filesize($realpath)));
#ini_set('zlib.output_compression', 0);
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");;
header("Content-Disposition: attachment;filename=events.xls");
header("Content-Transfer-Encoding: binary ");
header("Pragma: public");
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: pre-check=0, post-check=0, max-age=0');
header ("Pragma: no-cache");
header("Expires: 0");
header("Content-Description: File Transfer");
header("Content-Type: text/csv");
header("Content-Length: ". $size);
// also tried with having a flush(); here
// also tried with file_get_contents();
//also tried with wrapping the file_get_contents() or readfile() call inside ob_start() and ob_flush()+ob_clean()
readfile($realpath);
exit;
?>
Here is the wget output
wget "http://domain.com/test.php"
--2011-06-26 19:47:55-- http://domain.com/test.php
Resolving domain.com... 69.117.110.115
Connecting to domain.com|69.117.110.115|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1139844 (1.1M) [text/csv]
Saving to: `test.php'
3% [===> ] 36,373 --.-K/s in 11s
2011-06-26 19:48:11 (3.29 KB/s) - Connection closed at byte 36373. Retrying.
--2011-06-26 19:48:12-- (try: 2) http://domain.com/test.php
Connecting to domain.com|69.117.110.115|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1139844 (1.1M) [text/csv]
Saving to: `test.php'
3% [===> ] 40,469 --.-K/s in 11s
2011-06-26 19:48:24 (3.66 KB/s) - Connection closed at byte 40469. Retrying.
If i remove the header() required to offer a download and just echo the contents, then Chrome shows that the test.php was around 1.09M plus some more request, adds upto 1.1M[even in this case wget of test.php shows same behaviour as above], while firefox firebug shows that request was between 140K-300K[still not displaying all contents.
Check http://www.php.net/manual/en/function.readfile.php and search for: large. Copy-paste the code :)

php force download on mobile browsers

I'm trying to force download a image file (jpg for example) using php. So I have a script here force.php and here would be the code:
header("Pragma: public"); // required
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false); // required for certain browsers
header('Content-Description: File Transfer');
header("Content-Type: image/jpeg");
header('Content-Length: ' . filesize($file));
header('Content-Disposition: attachment; filename=test.jpg');
readfile($file);
Now the problem is for some browsers (mobile phone browsers especially), it'll work properly and prompt test.jpg for the user to save. However on some browser, it'll prompt force.php as download. Any solution?
Thank you!
Content disposition header highly depends on how a particular browser implements it. Sometimes there are encoding issues (I do not see in your case).
The document at Test Cases for HTTP Content-Disposition shows behavior for various browsers.
To be safe with mobile browsers you should consider having the http request's last part be equal to the actual filename of the attachment, for example http://some.url/download/test.jpg .
Use apache mod_rewrite or similar to route such requests to your download script.

Illustrator opening as PDF

I am coding a file sharing application for my office. One strange problem I am going through is the Illustrator files being opened in PDF when you hit the download button.
This problem is triggered because the mime type of illustrator files is application/pdf. So the browser when it reads the file, triggers Acrobat to open the file. Is there any way I could instruct the browser to open the file in Illustrator?
Or is there any way to modify the mime type after uploading the file? The backend code is PHP.
Thank you for any help.
One way to do this is to force the browser to display the "download file"-dialog. So the user can decide what to do with the file.
This can be done via PHP-Headers. (http://www.php.net/manual/en/function.header.php#83384)
There is also an example on how to this (Post 83384):
<?php
// downloading a file
$filename = $_GET['path'];
// fix for IE catching or PHP bug issue
header("Pragma: public");
header("Expires: 0"); // set expiration time
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
// browser must download file from server instead of cache
// force download dialog
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
// use the Content-Disposition header to supply a recommended filename and
// force the browser to display the save dialog.
header("Content-Disposition: attachment; filename=".basename($filename).";");
/*
The Content-transfer-encoding header should be binary, since the file will be read
directly from the disk and the raw bytes passed to the downloading computer.
The Content-length header is useful to set for downloads. The browser will be able to
show a progress meter as a file downloads. The content-lenght can be determines by
filesize function returns the size of a file.
*/
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filename));
#readfile($filename);
exit(0);
?>
When using this example please consider that using
$filename = $_GET['path'];
is a big security problem. You should work with something like ID's instead or validate the input.
For example:
if($_GET['file'] == 1) {
$filename = foobar.pdf;
} elseif($_GET['file'] == 2) {
$filename = foo.pdf;
} else {
die();
}

Categories