PHP served video display on Apple products - php

I'm trying to show a video on my Mac and I phone but I can't get it to work. My videos are served by php, I've been reading it has to do with range headers not being sent but I can't figure out how to do it correctly.
Heres what I have so far:
HTML:
<video controls="true" autoplay muted loop>
<source src="http://my_site.com/video/394934" type="video/mp4">
</video>
PHP:
// Get file GUID
$file_guid = (int) get_input('file_guid', 0);
// Get file thumbnail size
$size = get_input('size', 'small');
$file = get_entity($file_guid);
if (!elgg_instanceof($file, 'object', 'file')) {
exit;
}
$simpletype = $file->simpletype;
if ($simpletype == "image") {
// Get file thumbnail
switch ($size) {
case "small":
$thumbfile = $file->thumbnail;
break;
case "medium":
$thumbfile = $file->smallthumb;
break;
case "large":
default:
$thumbfile = $file->largethumb;
break;
}
// Grab the file
if ($thumbfile && !empty($thumbfile)) {
$readfile = new ElggFile();
$readfile->owner_guid = $file->owner_guid;
$readfile->setFilename($thumbfile);
$mime = $file->getMimeType();
$contents = $readfile->grabFile();
// caching images for 10 days
header("Content-type: $mime");
header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', strtotime("+10 days")), true);
header("Pragma: public", true);
header("Cache-Control: public", true);
header("Content-Length: " . strlen($contents));
echo $contents;
exit;
}
}

if it is in fact a range headers issue, there's a github project with a code example tackling this issue for Safari/Mac:
https://gist.github.com/codler/3906826

Related

Why does php download stop on large files?

I have a php script that allows users to download large files. The script works well except for files of over around 500mb. Everytime I try to download a file that is 664mb the download stops at about 460mb ( around 15-17 mins). There is no error. What can I do? I
suspect the script is timing out but I can't see why. I've spent days trying to get it to work and just can't make any progress. Any thoughts or suggestions would be great. I'm using a hosted server so cannot try modx_sendfile sadly. I'm using php 7.0.25
$db = new mysqli($dbHost, $dbUsername, $dbPassword, $dbName);
ob_start() or die('Cannot start output buffering');
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
$strDownloadFolder = "files/";
//- turn off compression on the server
//#apache_setenv('no-gzip', 1);
#ini_set('zlib.output_compression', 'Off');
//Download a file more than once
$boolAllowMultipleDownload = 2;
if(!empty($_GET['key'])){
//check the DB for the key
$sql = "SELECT * FROM downloads WHERE downloadkey = '".mysqli_real_escape_string($db,$_GET['key'])."' LIMIT 1";
$resCheck=mysqli_query($db, $sql);
$arrCheck = mysqli_fetch_array($resCheck); //Create array from first result
if(!empty($arrCheck['file'])){
//check that the download time hasnt expired
if($arrCheck['expires']>=time()){
if(!$arrCheck['downloads'] OR $boolAllowMultipleDownload){
//everything is ok -let the user download it
$strDownload = $strDownloadFolder.$arrCheck['file'];
if(file_exists($strDownload)){
$file_path = $arrCheck['file'];
$path_parts = pathinfo($file_path);
$file_name = $path_parts['basename'];
$file_ext = $path_parts['extension'];
$file_path = 'files/' . $file_name;
// allow a file to be streamed instead of sent as an attachment
$is_attachment = isset($_REQUEST['stream']) ? false : true;
// make sure the file exists
if (is_file($file_path))
{
$file_size = filesize($file_path);
$file = #fopen($file_path,"rb");
if ($file)
{
// set the headers, prevent caching
header("Pragma: public");
header("Expires: -1");
header("Cache-Control: public, must-revalidate, post-check=0, pre-check=0");
header("Content-Disposition: attachment; filename=\"$file_name\"");
// set appropriate headers for attachment or streamed file
if ($is_attachment) {
header("Content-Disposition: attachment; filename=\"$file_name\"");
}
else {
header('Content-Disposition: inline;');
header('Content-Transfer-Encoding: binary');
}
// set the mime type based on extension, add yours if needed.
$ctype_default = "application/octet-stream";
$content_types = array(
"exe" => "application/octet-stream",
"zip" => "application/zip",
"mp3" => "audio/mpeg",
"mpg" => "video/mpeg",
"avi" => "video/x-msvideo",
);
$ctype = isset($content_types[$file_ext]) ? $content_types[$file_ext] : $ctype_default;
header("Content-Type: " . $ctype);
//check if http_range is sent by browser (or download manager)
if(isset($_SERVER['HTTP_RANGE']))
{
list($size_unit, $range_orig) = explode('=', $_SERVER['HTTP_RANGE'], 2);
if ($size_unit == 'bytes')
{
list($range, $extra_ranges) = explode(',', $range_orig, 2);
}
else
{
$range = '';
header('HTTP/1.1 416 Requested Range Not Satisfiable');
exit;
}
}
else
{
$range = '';
}
//figure out download piece from range (if set)
list($seek_start, $seek_end) = explode('-', $range, 2);
//set start and end based on range (if set), else set defaults
//also check for invalid ranges.
$seek_end = (empty($seek_end)) ? ($file_size - 1) : min(abs(intval($seek_end)),($file_size - 1));
$seek_start = (empty($seek_start) || $seek_end < abs(intval($seek_start))) ? 0 : max(abs(intval($seek_start)),0);
//Only send partial content header if downloading a piece of the file (IE workaround)
if ($seek_start > 0 || $seek_end < ($file_size - 1))
{
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes '.$seek_start.'-'.$seek_end.'/'.$file_size);
header('Content-Length: '.($seek_end - $seek_start + 1));
}
else
header("Content-Length: $file_size");
header('Accept-Ranges: bytes');
set_time_limit(0);
fseek($file, $seek_start);
while(!feof($file))
{
print(#fread($file, 1024*8));
ob_flush();
flush();
if (connection_status()!=0)
{
#fclose($file);
exit;
}
}
// file save was a success
#fclose($file);
exit;
}
else
{
// file couldn't be opened
header("HTTP/1.0 500 Internal Server Error");
exit;
}
}
else
{
// file does not exist
header("HTTP/1.0 404 Not Found");
exit;
}
}else{
echo "We couldn't find the file to download.";
}
}else{
//this file has already been downloaded and multiple downloads are not allowed
echo "This file has already been downloaded.";
}
}else{
//this download has passed its expiry date
echo "This download has expired.";
}
}else{
//the download key given didnt match anything in the DB
echo "No file was found to download.";
}
}else{
//No download key wa provided to this script
echo "No download key was provided. Please return to the previous page and try again.";
}
A much simpler piece of code that still throws up the same problem (but works with smaller files) mkaes me think it is a hosting / php.ini issue although I don't know why.
<?php
$local_file = 'my_big_file.zip';
$download_file = 'my_big_file.zip';
// set the download rate limit (=> 20,5 kb/s)
if(file_exists($local_file) && is_file($local_file))
{
header('Cache-control: private');
header('Content-Type: application/octet-stream');
header('Content-Length: '.filesize($local_file));
header('Content-Disposition: filename='.$download_file);
flush();
$file = fopen($local_file, "r");
while(!feof($file))
{
// send the current file part to the browser
print fread($file, round(1024 * 1024));
// flush the content to the browser
flush();
// sleep one second
sleep(1);
}
fclose($file);}
else {
die('Error: The file '.$local_file.' does not exist!');
}
?>
This script stops downloading after about 17 minutes.

trouble of show http image in https

I need to embed http image in my https website, because if simply use <img src="http://www.sof.com/abc.jpg"/> will not show in https website.
After that, I was search some topic of this function, I found that something like <img src="https://www.some.com/image.php?url=http://www.sof.com/abc.jpg" /> can be show in https
So now I was genarate 2 source. 1 is the image file: http://www.website1.com/abc.png,
and the https web adress is https://fb.ccc.com
after that I create the image.php code was below:
<?
$strFile = base64_decode(#$_GET['url']);
$strFileExt = end(explode('.' , $strFile));
if($strFileExt == 'jpg' or $strFileExt == 'jpeg'){
header('Content-Type: image/jpeg');
}elseif($strFileExt == 'png'){
header('Content-Type: image/png');
}elseif($strFileExt == 'gif'){
header('Content-Type: image/gif');
}else{
die('not supported');
}
if($strFile != ''){
$cache_expire = 60*60*24*365;
header("Pragma: public");
header("Cache-Control: maxage=". $cache_expire);
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $cache_expire).' GMT');
}
exit;
?>
if I request the link: https://fb.ccc.com/image.php?url=http://www.website1.com/abc.png , then the page will be respone not supported
I was also try $strFile = $_GET['url']; but it only show blank.
if I try $strFile = var_dump(#$_GET['url']); and the page will respone string(31) "http://www.website1.com/abc.png" not supported
so someone can help to find out what is my script problem?
thank you very much!

Convert an external PHP script to an internal function

I have this short external php script (named resize.php) which I am calling to from other php scripts when I want to resize/thumbnail images.
I would like to change the script to work as an internal function instead of an external php script and I don't know how to convert it to a function that will do the job.
This is how it is being called:
<img src="resize.php?image=images/IMG228.jpg&width=165" border="0" alt="foo" width="165" height="165" title="">
The main reason I want to change it to be a function is because due to caching issues, when it is an external php script, if it has already been called/loaded by the browser, it doesn't load it again, even if the image on the server (to be resized) is newer, and despite the fact that the resizer.php itself had a caching detection and refresh implemented. If it is an internal resizer function, I assume this issue will be resolved.
This is the resizer.php script:
<?php
if (isset($_GET['image']) && isset($_GET['width']) && is_numeric($_GET['width'])) {
// Get image name
$original_image = $_GET['image'];
// Watermarks
$wmark = 'watermark.png'; //largest watermark
$wmarkm = 'watermark_m.png'; //medium watermark
$wmarks = 'watermark_s.png'; //smallest watermark
$wmarkno = 'nowatermark.png'; //No watermark
// Maximum image width
$max_width = (int) $_GET['width'];
// Maximum image height
$max_height = "800";
if (file_exists($original_image)) {
$cached = 'cache/' . preg_replace('/(\.\w+$)/', ".{$max_width}\\1", $original_image);
if (file_exists($cached)) {
$cst = stat($cached);
$fst = stat($original_image);
if ($fst[9] <= $cst[9] && $fst[10] <= $cst[10]) {
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $cst[9]) {
// header("HTTP/1.0 304 Not Modified");
header("HTTP/1.1 304 Not Modified");
// header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($cached)).' GMT', true, 304);
} else {
header('Content-type: image/jpeg');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $cst[9]) . ' GMT');
header('Cache-Control: private');
// print file_get_contents($cached);
readfile("{$cached}");
}
exit;
}
}
if ($max_width > 300) {
$watermark = $wmark;
} elseif ($max_width > 152 && $max_width < 300) {
$watermark = $wmarkm;
} elseif ($max_width > 50 && $max_width < 151) {
$watermark = $wmarks;
} else {
$watermark = $wmarkno;
}
//create the resized image
exec("gm convert -filter Lanczos {$original_image} -thumbnail {$max_width}x{$max_height} -quality 90 -unsharp 2x0.5+0.7+0 {$cached}");
//apply the watermark and recreate the watermarked image, overwriting the previously resized image
exec("gm composite -quality 90 -dissolve 100 -gravity center {$watermark} {$cached} {$cached}");
header('Content-type: image/jpeg');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: private');
readfile("{$cached}");
}
}
?>
Like this?
img_resize.php
<?php
function resizeImage($image, $width)
{
if(file_exists('c:/server/path/to/images/folder.'.$image) && ctype_digit($width) && $width >= 1)
{
// awesome image resizing code
}
}
?>
some frontend script which the user uploads their picture to: resize.php ?
<?php
include_once('c:/path/to/img_resize.php');
resizeImage('user12345_profilepic.jpg', 165);
?>

Elgg Profile Image

I am developing a plugin in elgg which has a some images for profile icon.
in that plugin there are various sized file for a single image for eg
train.jpg = Height : 100px || Width : 100px
train_25px.jpg = Height : 25px || Width : 25px
Like above i have cropped images. there are about 25 images + 25 cropped images
i want to define one image per user and call the image at any time without recreating the same image in another location
for eg :
Original Location : sitepath/mod/plugin_name/graphic/profile_icon1/master.jpg
User Image Location : site_temp_path/year/month/user_guid/master.jpg
My php function is
global $CONFIG;
function identicon_init() {
extend_view('profile/editicon', 'identicon/editicon');
register_action('identicon/preference', false, $CONFIG->pluginspath . 'identicon/actions/preference.php');
register_plugin_hook('entity:icon:url', 'user', 'identicon_usericon_hook', 900);
}
function identicon_usericon_hook($hook, $entity_type, $returnvalue, $params) {
if (($hook == 'entity:icon:url') && ($params['entity'] instanceof ElggUser)) {
$ent = $params['entity'];
if ($ent->preferIdenticon || !$returnvalue) {
return identicon_url($ent, $params['size']);
}
} else {
return $returnvalue;
}
}
function identicon_url($ent, $size) {
global $CONFIG;
return $CONFIG->wwwroot . 'mod/fp_auto_profile_image/img.php?entity=' . $ent->getGUID() . '&size=' . $size;
}
register_elgg_event_handler('init','system','identicon_init');
use elgg_get_data_path() instead of $CONFIG->wwwroot
then create icon.php and return path to icon.php in identicon_usericon_hook along with required with and size
then in icon.php have this code
$news_guid = get_input('guid');
$contents = readfile(filepath);
$size = strtolower(get_input('size'));
if (!in_array($size, array('large', 'medium', 'small', 'tiny', 'master', 'topbar')))
$size = "large";
header("Content-type: image/jpeg");
header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', strtotime("+10 days")), true);
header("Pragma: public");
header("Cache-Control: public");
header("Content-Length: " . strlen($contents));
header("ETag: \"$etag\"");
echo $contents;

Refresh in download script does not work

I have this download script which works perfectly, but one the file is downloaded, I want the page to reload. But once the file is downloaded, the page just dies? It is supposed to be a secure download script so users can't see the paths of files. It's called by using a POST submission
<?php
session_start();
//Assign variables from session
$userid = $_SESSION["id"];
$email = $_SESSION['email'];
$password = $_SESSION['password'];
//Require database connection and functions
require('includes/config.php');
include('includes/functions.php');
//Check if user is logged in
if (!$userid || !$email || !$password)
{
header("Location: index.php");
}
// Usage: Download
// Path to downloadable files (will not be revealed to users so they will never know your file's real address)
$hiddenPath = "uploaded_notes/";
// VARIABLES
if (!empty($_POST['file'])){
$file_id = $_POST['file'];
$result = mysql_query("SELECT * FROM files WHERE id='$file_id'");
$row = mysql_fetch_array($result);
$file = $row['location'];
$price = $row['price'];
$result2 = mysql_query("SELECT coins FROM users WHERE id='$userid'");
$row2 = mysql_fetch_array($result2);
$coins = $row2['coins'];
$new_coins = $coins-$price;
if ($new_coins >= 0)
{
mysql_query("UPDATE users SET coins = $new_coins WHERE id='$id'");
}
else {
header("Location: dashboard.php");
die();
}
//Insert into purchases
$datetime = date("Y-m-d H:i:s");#
mysql_query("INSERT INTO purchases (userid, fileid, datetime) VALUES ('$userid', '$file_id', '$datetime')");
$file = str_replace('%20', ' ', $file);
$category = (!empty($_GET['category'])) ? $_GET['category'] . '/' : '';
} else {
header('Location: dashboard.php');
die('Hacking attempt reported.');
}
$file_real = $hiddenPath . $category . $file;
$ip = $_SERVER['REMOTE_ADDR'];
// If requested file exists
if (file_exists($file_real)){
// Get extension of requested file
$extension = strtolower(substr(strrchr($file, "."), 1));
// Determine correct MIME type
switch($extension){
case "asf": $type = "video/x-ms-asf"; break;
case "avi": $type = "video/x-msvideo"; break;
case "exe": $type = "application/octet-stream"; break;
case "mov": $type = "video/quicktime"; break;
case "mp3": $type = "audio/mpeg"; break;
case "mpg": $type = "video/mpeg"; break;
case "mpeg": $type = "video/mpeg"; break;
case "rar": $type = "encoding/x-compress"; break;
case "txt": $type = "text/plain"; break;
case "wav": $type = "audio/wav"; break;
case "wma": $type = "audio/x-ms-wma"; break;
case "wmv": $type = "video/x-ms-wmv"; break;
case "zip": $type = "application/x-zip-compressed"; break;
case "jpg": $type = "image/jpeg"; break;
default: $type = "application/force-download"; break;
}
// Fix IE bug [0]
$header_file = (strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) ? preg_replace('/\./', '%2e', $file, substr_count($file, '.') - 1) : $file;
// Prepare headers
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: public", false);
header("Content-Description: File Transfer");
header("Content-Type: " . $type);
header("Accept-Ranges: bytes");
header("Content-Disposition: attachment; filename=\"" . $header_file . "\";");
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . filesize($file_real));
// Send file for download
if ($stream = fopen($file_real, 'rb')){
while(!feof($stream) && connection_status() == 0){
//reset time limit for big files
print(fread($stream,1024*8));
flush();
}
fclose($stream);
}
}else{
// Requested file does not exist (File not found)
echo("Requested file does not exist");
die();
}
?>
The refresh code works if I put it right before
print(fread($stream,1024*8));
But after that it does not work...
Downloading a file is a one time thing. You won't be able to redirect after the file is downloaded. You can try adding a refresh header before pumping out your contents, but some browsers will see that as a cancellation.
See this post for more information: https://stackoverflow.com/questions/5960895/redirect-after-download-not-working-php
You can read this post, this post, and this post for more information.
Basically, what they all boil down to is the HTTP headers. A redirect header cannot be sent after you have echoed content. On the other hand, a redirect header before echoing content will just cancel the download.
A different way you could do this would be to have a hidden iframe, and submit your request there, but I am guessing that isn't what you are wanting.

Categories