I'm using Apache's mod_rewrite to route requests for JPG files to a directory outside my web root.
It generally has been fine, but there are a few images that do not display. I then realized that when I use PHP's get_headers() function on my image URLs, they are all returning
Content-Type: text/html; charset=UTF-8 instead of the proper image/jpeg header types.
I have tried explicitly setting the Content-Type: image/jpeg header and still, none of my images return the correct headers - although most do display correctly, but I'm not sure why.
How can I assure a JPG file is sent with the correct header when redirecting via mod_rewrite?
This is what you could do. Create a PHP file that will get the right file and passes it through
<?php
$sImage = 'imagename.jpg';
header("Content-Type: image/jpeg");
header("Content-Length: " .(string)(filesize($sImage)) );
echo file_get_contents($sImage);
or
<?php
$sImage = 'imagename.jpg';
$rFP = fopen($sImage, 'rb');
header("Content-Type: image/jpeg");
header("Content-Length: " .(string)(filesize($sImage)) );
fpassthru($rFP);
exit;
or in your Apache vhost config or .htaccess file
RewriteRule … … [T=image/jpeg]
You can also set the Content-Type header field with mod_rewrite with the T flag:
RewriteRule … … [T=image/jpeg]
How about image which is not.jpg. Like .gif, ...
You'll need to use mime_content_type() (which is deprecated) or the fileinfo extension to determine which content-type to send.
Edit: I don't recommend this, but if you are working with a specific subset of file extensions, you could also create a small dictionary array of content-types and use the file extension to determine which one to send.
Related
I have a php file where everything works fine, but If I set the header to:
header("Content-type: xml/text");
Like it should be, my browser just tries to download it. It works with curl, but not Safari. It's weird because I have a lot of files that have that header on my server and they work, I even created a test php and set the header to that and it worked. How can I debug why that specfic page downloads when I set the header?
Try
header("Content-type: text/xml");
text/xml is correct mime-type. Different user agents likely handle the invalid xml/text mime type differently, resulting in weirdness.
(More precisely, use text/xml if there is some intention that the XML could be readable by a human and use application/xml if not. Both these two are valid mime types for XML.)
I have a directory in my web app where I want to keep all the user profile pictures. Users can upload all types of images (png, jpg, gif). However, I want the image URL to be friendly, eg. http://example.com/static/picture/300-username, where the file is 300-username, but with no extension. I thought of removing the extension when the user uploads, and with a PHP controller, add a:
header('Content-Type: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png');
readfile('http://example.com/static/picture/300-username');
This has worked well. I was wondering however, if this can be done by placing an .htaccess file in the picture directory, with some sort of Header, that whatever file is read in this directory, will display as picture. Is this possible?
Don’t remove the extension, just offer extensionless URIs like /static/picture/300-username. Then add a line to your .htaccess:
Options +MultiViews
Apache will set the correct content type for you. See the documentation for Content Negotiation.
You can't specify multiple Content-Type like that.. You need to pass the correct header for each image served:
<?php
$user = '300-username';
$picture = 'http://example.com/static/picture/' . $user;
$info = getimagesize($picture);
$fp = fopen($picture, "rb");
if ($info && $fp) {
header("Content-type: {$info['mime']}");
fpassthru($fp);
exit;
} else {
// error
}
?>
(Code adapted from PHP's getimagesize() documentation.)
First your path need to be physically existent, else I don't think Apache can do anything for you.
However, here may be a workaround using a RewriteRule, keep the extension (by the way, I think it's bad practice to remove extension of a file, especially when they are not plain text files), and accept a requested file like 300-username to rewrite to 300-username.jpg.
I have list of images and I want a "Download" link along with every image so that user can download the image.
so can someone guide me How to Provide Download link for any file in php?
EDIT
I want a download panel to be displayed on clicking the download link I dont want to navigate to image to be displayed on the browser
If you want to force a download, you can use something like the following:
<?php
// Fetch the file info.
$filePath = '/path/to/file/on/disk.jpg';
if(file_exists($filePath)) {
$fileName = basename($filePath);
$fileSize = filesize($filePath);
// Output headers.
header("Cache-Control: private");
header("Content-Type: application/stream");
header("Content-Length: ".$fileSize);
header("Content-Disposition: attachment; filename=".$fileName);
// Output file.
readfile ($filePath);
exit();
}
else {
die('The provided file path is not valid.');
}
?>
If you simply link to this script using a normal link the file will be downloaded.
Incidentally, the code snippet above needs to be executed at the start of a page (before any headers or HTML output had occurred.) Also take care if you decide to create a function based around this for downloading arbitrary files - you'll need to ensure that you prevent directory traversal (realpath is handy), only permit downloads from within a defined area, etc. if you're accepting input from a $_GET or $_POST.
In HTML5 download attribute of <a> tag can be used:
echo '<a href="path/to/file" download>Download</a>';
This attribute is only used if the href attribute is set.
There are no restrictions on allowed values, and the browser will
automatically detect the correct file extension and add it to the file
(.img, .pdf, .txt, .html, etc.).
Read more here.
The solution is easier that you think ;) Simple use:
header('Content-Disposition: attachment');
And that's all. Facebook for example does the same.
You can do this in .htaccess and specify for different file extensions. It's sometimes easier to do this than hard-coding into the application.
<FilesMatch "\.(?i:pdf)$">
ForceType application/octet-stream
Header set Content-Disposition attachment
</FilesMatch>
By the way, you might need to clear browser cache before it works correctly.
I have a small script, which reads the data from DB, array them and save them as .txt file.
At the end, user is redirected to that file.
Now, how to achieve that when user is redirected on .txt file download box appears?
<?php
// Preden zacnemo, dobi novico iz baze!
$MOD_NEWS_SAVETXT_getnews = mysql_query("SELECT * FROM NEWS WHERE NEWSid = '{$_GET['id']}'") or die(mysql_error());
// Nardimo while in priredimo vsebino iz baze spremenljivkam!
while ($MOD_NEWS_SAVETXT_NEWSRESULT = mysql_fetch_array($MOD_NEWS_SAVETXT_getnews)) {
$MOD_NEWS_SAVETXT_FILE_name = $MOD_NEWS_SAVETXT_NEWSRESULT['NEWStitle'] . ".txt";
echo $MOD_NEWS_SAVETXT_FILE_name;
$MOD_NEWS_SAVETXT_FILE_handle = fopen($MOD_NEWS_SAVETXT_FILE_name, 'w') or die("Ne morem brati/ustvariti datoteke!");
fwrite($MOD_NEWS_SAVETXT_FILE_handle, $MOD_NEWS_SAVETXT_NEWSRESULT['NEWStext']);
fclose($MOD_NEWS_SAVETXT_FILE_handle);
header("Location: ./" . $MOD_NEWS_SAVETXT_FILE_name ."");
}
?>
This has come up before:
forcing a file to download
forcing a file download with php
How to Automatically Start a Download in PHP?
Correct way to trigger a file download (in PHP)?
To force a file to download, you have to send the HTTP Headers that will tell the browser to treat that file as a download. That is override the Content-Type of the file.
http://en.wikipedia.org/wiki/List_of_HTTP_headers
Basically, when your web server retrieves a file requested by the browser, it first checks the file extension and guesses the mime type. It then creates the HTTP response, and inserts the Content-Type header with the value of the mime-type it guessed.
You can have PHP set the content-type of the file explicitly.
The example is already given above.
header("Content-type: application/force-download");
or
header("Content-type: application/octet-stream");
For downloads, you also have to set the Content-Disposition and Content-Transfer-Encoding HTTP response headers.
These are defined in the MIME specifications: http://www.faqs.org/rfcs/rfc1521.html
It is also good to define the content-length as this allows the browser to give the user a download progress bar.
You may also want to look at using the range headers to allow request in parts.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
I want to monitor how often some external images are loaded.
So my idea is instead of giving a uri directly like this:
www.site.com/image1.jpg
I can create a PHP script which reads the image, so I built a PHP file and my HTML would look like this:
<img src="www.site.com/serveImage.php?img=image1.jpg">
but I don't know how to read the image from disk and return it. Would I return a byte array or set the content type?
Kind regards,
Michel
Sending images through a script is nice for other things like resizing and caching on demand.
As answered by Pascal MARTIN the function readfile and these headers are the requirements:
Content-Type
The mime type of this content
Example: header('Content-Type: image/gif');
See the function mime_content_type
Types
image/gif
image/jpeg
image/png
But beside the obvious content-type you should also look at other headers such as:
Content-Length
The length of the response body in octets (8-bit bytes)
Example: header('Content-Length: 348');
See the function filesize
Allows the connectio to be better used.
Last-Modified
The last modified date for the requested object, in RFC 2822 format
Example: header('Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT');
See the function filemtime and date to format it into the required RFC 2822 format
Example: header('Last-Modified: '.date(DATE_RFC2822, filemtime($filename)));
You can exit the script after sending a 304 if the file modified time is the same.
status code
Example: header("HTTP/1.1 304 Not Modified");
you can exit now and not send the image one more time
For last modified time, look for this in $_SERVER
If-Modified-Since
Allows a 304 Not Modified to be returned if content is unchanged
Example: If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
Is in $_SERVER with the key http_if_modified_since
List of HTTP header responses
To achieve something like this, your script will need to :
send the right headers, which depend on the type of the image : image/gif, image/png, image/jpeg, ...
send the data of the image
making sure nothing else is sent (no white space, no nothing)
This is done with the header function, with some code like this :
header("Content-type: image/gif");
Or
header("Content-type: image/jpeg");
or whatever, depending on the type of the image.
To send the data of the image, you can use the readfile function :
Reads a file and writes it to the
output buffer.
This way, in one function, you both read the file, and output its content.
As a sidenote :
you must put some security in place, to ensure users can't request anything they want via your script : you must make sure it only serves images, from the directory you expect ; nothing like serveImage.php?file=/etc/passwd should be OK, for instance.
If you're just willing to get the number of times a file was loaded each day, parsing Apache's log file might be a good idea (via a batch run by cron each day at 00:05, that parses the log of the day before, for instance) ; you won't have real-time statistics, but it will require less resources on your server (no PHP to serve static files)
I use the "passthru" function to call "cat" command, like this:
header('Content-type: image/jpeg');
passthru('cat /path/to/image/file.jpg');
Works on Linux. Saves resources.
You must set the content type:
header("Content-type: image/jpeg");
Then you load the image and output it like this:
$image=imagecreatefromjpeg($_GET['img']);
imagejpeg($image);
Instead of changing the direct image url in the HTML, you can put a line in the Apache configuration or .htaccess to rewrite all the requests of images in a directory to a php script. Then in that script you can make use of the request headers and the $_server array to process the request and serve the file.
First in your .htaccess:
RewriteRule ^(.*)\.jpg$ serve.php [NC]
RewriteRule ^(.*)\.jpeg$ serve.php [NC]
RewriteRule ^(.*)\.png$ serve.php [NC]
RewriteRule ^(.*)\.gif$ serve.php [NC]
RewriteRule ^(.*)\.bmp$ serve.php [NC]
The script serve.php must be in the same directory as .htaccess. You will probably write something like this:
<?php
$filepath=$_SERVER['REQUEST_URI'];
$filepath='.'.$filepath;
if (file_exists($filepath))
{
touch($filepath,filemtime($filepath),time()); // this will just record the time of access in file inode. you can write your own code to do whatever
$path_parts=pathinfo($filepath);
switch(strtolower($path_parts['extension']))
{
case "gif":
header("Content-type: image/gif");
break;
case "jpg":
case "jpeg":
header("Content-type: image/jpeg");
break;
case "png":
header("Content-type: image/png");
break;
case "bmp":
header("Content-type: image/bmp");
break;
}
header("Accept-Ranges: bytes");
header('Content-Length: ' . filesize($filepath));
header("Last-Modified: Fri, 03 Mar 2004 06:32:31 GMT");
readfile($filepath);
}
else
{
header( "HTTP/1.0 404 Not Found");
header("Content-type: image/jpeg");
header('Content-Length: ' . filesize("404_files.jpg"));
header("Accept-Ranges: bytes");
header("Last-Modified: Fri, 03 Mar 2004 06:32:31 GMT");
readfile("404_files.jpg");
}
/*
By Samer Mhana
www.dorar-aliraq.net
*/
?>
(This script can be improved!)
Also, if you want to the user to see a real filename instead of your scriptname when the user RMC's on the image and selects "Save As", you'll need to also set this header:
header('Content-Disposition: filename=$filename');
You're probably better off examining your server access logs for this. Running all images through php might put a bit of load on your server.
I serve my images with readfile as well, but I have gone the extra mile both for security and extra functionality.
I have a database set up which stores the image id, its dimensions and file extension. This also means that images need to be uploaded (allowing optional resizing), so I only use the system for content and not images needed for the website itself (like backgrounds or sprites).
It also does a very good job at making sure you can only request images.
So, for serving the simplified workflow would be like this (cannot post production code here):
1) get the ID of the requested image
2) Look it up in the database
3) Throw headers based on the extension ("jpg" gets remapped to "jpeg" on upload)
4) readfile("/images/$id.$extension");
5) Optionally, protect /images/ dir so it cannot be indexed (not a problem in my own system as it maps URLS like /image/view/11 to something like /index.php?module=image&action=view&id=11)
There are a lot of good answers above, but none of them provide working code that you can use in your PHP app. I've set mine up so that I lookup the name of the image in a database table based off a different identifier. The client never sets the name of the file to download as this is a security risk.
Once the image name is found, I explode it to obtain the extension. This is important to know what type of header to serve based off the image type (i.e. png, jpg, jpeg, gif, etc.). I use a switch to do this for security reasons and to convert jpg -> jpeg for the proper header name. I've included a few additional headers in my code that ensure the file is not cached, that revalidation is required, to change the name (otherwise it will be the name of the script that is called), and finally to read the file from the server and transmit it.
I like this method since it never exposes the directory or actual file name. Be sure you authenticate the user before running the script if you are trying to do this securely.
$temp = explode('.', $image_filename);
$extension = end($temp); // jpg, jpeg, gif, png - add other flavors based off your use case
switch ($extension) {
case "jpg":
header('Content-type: image/jpeg');
break;
case "jpeg":
case "gif":
case "png":
header('Content-type: image/'.$extension);
break;
default:
die; // avoid security issues with prohibited extensions
}
header('Content-Disposition: filename=photo.'.$extension);
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
readfile('../SECURE_DIRECTORY/'.$image_filename);
PHP 8 lets you use the match feature, which will further optimize the code by getting rid of the switch and ugly looking nested cases.