304 Not modified response when serving files through PHP - php

I have a PHP file serving image files. I looks like this:
$dir = 'directory/containing/files';
$file = getFileInfoFromDatabase(filter_input(INPUT_GET, 'article'));
header("Content-Type: $file->type; name=\"$file->filename\"");
header("Name: \"$file->filename\"");
header("Content-Disposition: inline; filename=\"$file->filename\"");
header("Content-Length: $file->size");
die(readfile($dir.'/'.$file->id));
An url calling this file looks like this: www.example.com/article/image.php?article=4. 4 is the id of the article, which is sent to the database to get information stored there about the image file associated with that article.
This works well. When loading the url, I get the image and some response headers looking like this:
Cache-Control:private, max-age=10800, pre-check=10800
Connection:Keep-Alive
Content-Disposition:inline; filename="filename.jpg"
Content-Length:37308
Content-Type:image/jpeg; name="filename.jpg"
Date:Wed, 30 Sep 2015 08:42:26 GMT
Keep-Alive:timeout=5, max=75
Last-Modified:Wed, 30 Sep 2015 08:32:19 GMT
Name:"filename.jpg"
Server:Apache
The next time I try loading the url, the request contains the following header:
If-Modified-Since:Wed, 30 Sep 2015 08:32:19 GMT
And I get a 304 Not Modified response, which is fine.
However, if I update the database to point to a different file, I get the same 304 Not Modified response! I have to modify the php file serving the image file to get the new version of the image file.
How can I solve this? Do I have to turn off caching for this file? Or can I change the Last-Modified header to be that of the image file instead of the php file in some way?

If you know the modification time of the changed file, you can change the last_modified header if the request If-Modified-Since is older.

Related

Setting up cache to consider file-modification date/time

I'm having problems with serving CSS files from PHP. For test I'm just loading content from existing CSS file into PHP variable and than echo it. I want to set headers to allow caching of file until it was modified.
PHP code
$css_file_path = "path-to-existing-css-file";
$file_content = file_get_contents ($css_file_path);
$fmtime = date ("r", filemtime ($css_file_path));
header ("Content-type: text/css");
header ("X-Content-Type-Options: nosniff");
header ("Last-Modified: " . $fmtime);
die ($file_contents);
This is done by simple PHP code as shown above. For some reason it's never cached (tested in latest Firefox only).
I have tried to put this line before die() function to test it.
echo date ("r", time());
And it gets updated all the time. I'm such a caching noob, I admit it, so all I want to do is to make file being cached until new modification arrives.
So far, I have read tones of different posts here and web-wide and mostly found nothing or very poor information on this subject.
What am I missing and is it possible to achieve at all?
To start with
I want to do is to make file being cached until new modification arrives
The only way a browser can know there is a new modification, is by asking the server whether their cached version is still valid.
This is done as followed:
1. Browser requests /style.css
GET /style.css
2. Server sends to browser
HTTP/1.1 200 OK
Last-Modified: Wed 2 Aug 2017 21:28:00 GMT
Cache-Control: must-revalidate, max-age=31536000
... file-contents ...
// 31536000 is about 1 year
3. Next time browser wants that file it sends
GET /style.css
If-Modified-Since: Wed 2 Aug 2017 21:28:00 GMT
4a. Your server can read that header, and verify if the file isn't modified after
the given date. If it isn't, you can reply with a single:
HTTP/1.1 304 Not Modified
... without sending the contents again
4b. If your file was hower modified after Aug 2, you should sent a response simalar
as in step 2
So in code, step 2, add the Cache-Control-header:
header('Cache-Control: must-revalidate, max-age=31536000');
And step 4a, act to the If-Modified-Since request-header:
$css_file_path = "path-to-existing-css-file";
$fmtimestamp = filemtime ($css_file_path);
// Check header set by browser
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $fmtimestamp <= strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified');
die(); // We're done here
}
// Otherwise continue as ussualy
$file_content = file_get_contents ($css_file_path);
Alternative solution, without using the If-Modified-Since, but it depends on the situation if this is usable for you:
// Somewhere in your HTML
<link rel="stylesheet" href="/style.css?version=<?php echo filemtime($pathToStyle.css) ?>" />
When your file changes, the link changes and the browser would see it as a new file. In that case you can leave the must-revalidate-part out of the Cache-Control-header and the browser won't reload the style.css unless the max-age expires or cache is cleaned up.

CakePHP 3 image download corrupt

I have followed Cakes Cookbook to send files (http://book.cakephp.org/3.0/en/controllers/request-response.html#sending-files) but I've been facing a weird problem.
PDF's, DOC's and other binaries work just fine. But when I try to download/show an image (JPG or PNG) the file corrupts itself.
The downloaded file is not recognized as an image. It has the exactly same size of the original one, but when I diff it they are completly different.
I couldn't find anything like at the internet related to cake so I hope you can help me!
The below code is my download action
public function arquivo($id) {
$file = $this->Arquivos->get( $id );
$this->response->file($file['filename'], ['download' => true]);
// Return response object to prevent controller from trying to render
// a view.
return $this->response;
}
Response headers:
Accept-Ranges:bytes
Cache-Control:no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Connection:Keep-Alive
Content-Length:121000
Content-Type:image/jpeg
Date:Thu, 24 Nov 2016 16:17:49 GMT
Expires:Thu, 19 Nov 1981 08:52:00 GMT
Keep-Alive:timeout=5, max=100
Pragma:no-cache
Server:Apache/2.4.10 (Ubuntu)
Check your file path and MIME Types is correct.
public function arquivo($id) {
$file = $this->Arquivos->get( $id );
$this->response->file(
$file['filename'], #Check $file['filename'] is full path of your download file
[
'download' => true,
'name' => 'Your_Download_File_Name_Here'
]
);
return $this->response;
}
Example:
Your $file['filename'] variable should be /path/to/your/file.jpg
Also check the correct MIME TYPES from CakePHP 3 Sending or Downloading Files
I've got the same problem with downloading jpg and xlsx files.
My code is quite simple:
public function download() {
$response = $this->response->withFile($template_file_path, ['download' => true]);
return $response;
}
Compared with 2 files (original file AND the downloaded file which is corrupted) with a binary editor, there is 1 byte difference. I don't know why but the 0x20 was added at the top of the file.
I've got the same problem and I was able to solve it. The problem was caused as soon as a recent change on my source code for another controller was made so I started searching for spaces after or before the opening php tag<?php <?. Also I deleted the closures ?> for all of my php files. I know it is hard to detect where the problem comes from but make sure to check the last files you changed. i.e controllers, behaviors, models etc.
I hope it can solve your problem.

Cache PHP page that echoes CSS code

Following the accepted answer here on SO, I am trying to create a stylesheet that is editable with PHP.
What's happening...
I am trying to make the stylesheet (named css.php) cache in the user's browser so that he/she does not have to load it on every pageload, and have set the following headers to do so:
header('Content-Type: text/css;;charset=UTF-8');
header('cache-control: max-age=86400;');
header('Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT;');
header('Expires: Sat, 22 Nov 2014 16:00:00 GMT;');
header('cache-control: max-age=86400;');
These are the other headers sent by default:
Connection:"Keep-Alive"
Content-Encoding:"gzip"
Content-Length:"393"
Date:"Fri, 21 Nov 2014 17:00:50 GMT"
Keep-Alive:"timeout=5, max=99"
Server:"Apache"
Vary:"Accept-Encoding"
X-Frame-Options:"SAMEORIGIN"
However, upon loading a page that references the css.php page multiple times, it continues to reload the CSS page every time.
How do I know this...
I am receiving a hit to the css.php page every time I load the page which uses the stylesheet on my apache server access logs.
I can see that my Firefox browser is accessing the css.php page in the Inspect Element tool. It is receiving a HTTP 200 every time.
What should I do?
Instead of creating a dynamic CSS file, change the standard one each time with PHP's file_put_contents() function.
Example:
file_put_contents("styles.css", $css_input);
That way, browsers will cache the file like normal.
Example:
<link rel="stylesheet" type="text/css" href="styles.css"/>
When you make changes the the actual CSS, browsers will automatically load the new one upon restart.

PHPExcel: I need to create two workbooks on one submission [duplicate]

Use case: user clicks the link on a webpage - boom! load of files sitting in his folder.
I tried to pack files using multipart/mixed message, but it seems to work only for Firefox
This is how my response looks like:
HTTP/1.0 200 OK
Connection: close
Date: Wed, 24 Jun 2009 23:41:40 GMT
Content-Type: multipart/mixed;boundary=AMZ90RFX875LKMFasdf09DDFF3
Client-Date: Wed, 24 Jun 2009 23:41:40 GMT
Client-Peer: 127.0.0.1:3000
Client-Response-Num: 1
MIME-Version: 1.0
Status: 200
--AMZ90RFX875LKMFasdf09DDFF3
Content-type: image/jpeg
Content-transfer-encoding: binary
Content-disposition: attachment; filename="001.jpg"
<< here goes binary data >>--AMZ90RFX875LKMFasdf09DDFF3
Content-type: image/jpeg
Content-transfer-encoding: binary
Content-disposition: attachment; filename="002.jpg"
<< here goes binary data >>--AMZ90RFX875LKMFasdf09DDFF3
--AMZ90RFX875LKMFasdf09DDFF3--
Thank you
P.S. No, zipping files is not an option
Zipping is the only option that will have consistent result on all browsers. If it's not an option because you don't know zips can be generated dynamically, well, they can. If it's not an option because you have a grudge against zip files, well..
MIME/multipart is for email messages and/or POST transmission to the HTTP server. It was never intended to be received and parsed on the client side of a HTTP transaction. Some browsers do implement it, some others don't.
As another alternative, you could have a JavaScript script opening windows downloading the individual files. Or a Java Applet (requires Java Runtimes on the machines, if it's an enterprise application, that shouldn't be a problem [as the NetAdmin can deploy it on the workstations]) that would download the files in a directory of the user's choice.
Remember doing this >10 years ago in the netscape 4 days. It used boundaries like what your doing and didn't work at all with other browsers at that time.
While it does not answer your question HTTP 1.1 supports request pipelining so that at least the same TCP connection can be reused to download multiple images.
You can use base64 encoding to embed an (very small) image into a HTML document, however from a browser/server standpoint, you're technically still sending only 1 document. Maybe this is what you intend to do?
Embedd Images into HTML using Base64
EDIT: i just realized that most methods i found in my google search only support firefox, and not iE.
You could make a json with multiple data urls.
Eg:
{
"stamp.png": "data:image/png;base64,...",
"document.pdf": "data:application/pdf;base64,..."
}
(extending trinalbadger587's answer)
You could return an html with multiple clickable, downloadable, inplace data links:
<html>
<body>
<a download="yourCoolFilename.png" href="data:image/png;base64,...">PNG</a>
<a download="theFileGetsSavedWithThisName.pdf" href="data:application/pdf;base64,...">PDF</a>
</body>
</html>

Caching generated images with PHP

I am trying to cache images which have been generated. You create a image by accessing the file by resize.php?width=x&height=y.
If the image of that width and height does not exist I use imagemagick to create it. However if it does exist it is served to the visitor.
The !file_exists($name) check works fine so processing is not done when its not needed. However the images still take a while to load.
Is my approach of readfile incorrect or have I set headers incorrectly?
if (!file_exists($name)) {
//image processing here
}
header("Content-Type: image/png");
header("Expires: Sat, 25 Jul 2020 10:00:00 GMT");
readfile($name);
Thanks.
If i had to do this, i'd proccess the image if it does not exist save it in some web accessible folder. If the file exists just redirect to it
header( 'Location: http://www.yoursite.com/path/to/existing/file.png' )

Categories