I have this PHP code and it works fine.
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="WTBak.zip"');
readfile($ArchiveFileName);
echo $ArchiveFileName;
unlink($ArchiveFileName);
My issue is, how do I give out a message after the last line (unlink) has been executed?
Thanks!
Assumptions:
The client user should receive a message, this is kind of message sent to the client
The response is binary
Abstract:
sending binary information to the client along with text response would be possible if it is mhtml format, used in mails and each browser has (some do nto have) the support for multipart response. Let us not chose this way
sending binary information to respond one request (download file) and another response to another request (status of download) - this is a popular practice.
Solution:
on server: persist the status of download
// pseudocode: log_download_event(seessionid, status='started')
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="WTBak.zip"');
readfile($ArchiveFileName);
echo $ArchiveFileName;
unlink($ArchiveFileName);
// pseudocode: log_download_event(seessionid, status='done')
on server: implement a php that will respond with a status of download
// pseudocode: $DownloadStatus=get_download_status(sessionid)
echo '{staus:' + $DownloadStatus + '}'
on client: on some event trigger the download
window.open("http://nowhere.com/download.php?resuorce=archive-file.zip");
window.theTrackInterval = window.setInterval(trackDownload, 1000);
var trackInterval = function(){
$.get('ajax/test.html', function(data) {
id(data.status=='ready'){
cleanInterval(window.theTrackInterval);
alert('download is done');
}
});
}
This solution will start sending the ajax requests to the server every one second asking "is download done" and when it will receive confirmation "yest it is done" client will stop tracking and alert a message
What is missed:
the implementation of status persistence. i am not PHP guy - forgive me this gap
Look, if you give a message, it will be sent in the file and not shown to the user corrupting / changing the contents of the file. You cannot modify the headers as they've already been sent!
So, I feel, there is now way!
Cheers
I had a similar requirement from client. So I created a tool to show message after a file gets downloaded.
You can see at http://www.iamkumaran.com/xdownloader-a-flash-javascript-library/ and look for demo link.
Related
I'm having trouble understanding Server Sent Events, particularly on the server side. All the examples I find are basically just sending the server time, which isn't terribly helpful.
I'm trying to bring data in from an external resource that is sending a POST request to the file, specifically a Twilio webhook whenever a new message is received.
The problem that I'm running into is that the data sent from the webhook isn't accessible from my request to the Server Sent Event, which makes sense when you think about it because they're two different sessions.
I could create create another file for the webhook to access and add the message data to an SQL database each time a new message is received. Then, in the file that is my serves as my EventSource, I could query that data and see if it's changed, but doesn't that seem horrible inefficient to be querying an SQL database every three seconds, even if I am comparing the data using a session variable or something?
Ideally, it would be something like this:
header('Content-Type: text/event-stream; charset=utf-8');
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
header("Access-Control-Allow-Origin: *");
$body = $_REQUEST['Body'];
$finalbody = !empty($body) ? $body : 'nothing to see here...';
echo "id: $id" . PHP_EOL;
echo "data: $finalbody" . PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
But that obviously doesn't work when you use it as the EventSource because $_REQUEST['Body'] can't be found.
What good is are Server Sent Events if they have to be constantly querying an external source rather than having data sent to them on-demand and then using that data to send the event on it's way?
Am I missing something?
Any insight here would be much appreciated.
I have a PHP script that serves portions of a PDF file by byte ranges.
If an HTTP HEAD request is received, it should send back headers (including the PDF file size) but not the actual file contents. I have tried this:
header('HTTP/1.1 200 OK');
header('Content-Type: application/pdf');
header('Accept-Ranges: bytes');
header('Content-Length: '.filesize($Pathname));
die;
The problem is that something (I assume the web server == LiteSpeed) replaces the Content-Length header with Content-Length: 0 - which defeats the whole purpose.
Can anyone suggest what I should be doing? Thanks
From w3c Hypertext Transfer Protocol -- HTTP/1.1:
When a Content-Length is given in a message where a message-body is
allowed, its field value MUST exactly match the number of OCTETs in
the message-body. HTTP/1.1 user agents MUST notify the user when an
invalid length is received and detected.
And:
The Content-Length entity-header field indicates the size of the
entity-body, in decimal number of OCTETs, sent to the recipient or, in
the case of the HEAD method, the size of the entity-body that would
have been sent had the request been a GET.
So, I suppose, your code will properly work if you send real HEAD request to your server.
It's the webserver job, not yours.
In my case I left everything to the Apache webserver and nothing changed in my php code except of how the requests is being parsed
For example things like
if($_SERVER['REQUEST_METHOD'] === "GET"){
//ok
}else{
//send 400 Bad Request
}
are changed to
if($_SERVER['REQUEST_METHOD'] === "GET" || $_SERVER['REQUEST_METHOD'] === "HEAD"){
//ok
}else{
//send 400 Bad Request
}
and Apache did all the heavy lifting (striped the response body).
(don't try to ob_clean() or die("") or things like this).
related resources:
http://hc.apache.org/httpclient-3.x/methods/head.html
https://security.stackexchange.com/questions/62811/should-i-disable-http-head-requests
Apache 2.2.2 response on HEAD requests
As Lurii mentioned, the content length is affected by your request type.
With GET requests, a non-matching content length may result in a hanging client, so LiteSpeed will verify the content length before sending the header to the client.
Using a HEAD request should return the content length as expected.
I'm trying to implement an upload module on a website which would allow our users to upload videos to our Vimeo account. I'm using blueimp's jQuery File upload and Vimeo's new API.
https://github.com/blueimp/jQuery-File-Upload/wiki/Options
https://developer.vimeo.com/api/upload#http-put-uploading
I think it's close to be working but I must be missing some detail.
According to Vimeo's API, I need to :
1. Generate an upload ticket, which works fine
2. I then pass the upload_link_secure to jquery file upload which starts uploading. This is what the requests headers for the PUT request look like :
Request Method:PUT
Status Code:200 OK
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
Connection:keep-alive
Content-Length:43418955
Content-Type:multipart/form-data; boundary=----WebKitFormBoundarye8sGy57JH6ACoOfJ
This is how I call jQuery file upload :
$('#file').fileupload({
url: upload_link_secure,
type: 'PUT'
});
I also tried forcing the Content-Type header to "video/mp4" but it doesn't make any difference in the end.
I also checked the size of the file by binding jquery fileupload's submit event and I also get a lower bytes count than what's sent in the headers, 43418764 in this example, is this okay ?
Verify the upload by sending PUT requests on upload_link_secure, some response headers I get :
Status Code:308 Resume Incomplete
Range:bytes=0-3948544
Status Code:308 Resume Incomplete
Range:bytes=0-38682624
Status Code:308 Resume Incomplete
Range:bytes=0-43401216
Make sure all the bytes made it to Vimeo, then complete the upload by sending a DELETE request on complete_uri
I get this last header when verifying the upload :
Range:bytes=0-43418955
It seems to match the Content-Length send in the first request so I perform a DELETE request, and this is the reponse I get :
{"body":{"error":"Your video file is not valid. Either you have uploaded an invalid file format, or your upload is incomplete. Make sure you verify your upload before marking it as complete."},"status":400,"headers":{"Date":"Mon, 06 Oct 2014 17","Server":"Apache","Vary":"Accept,Vimeo-Client-Id,Accept-Encoding","Cache-Control":"no-cache, max-age=315360000","Expires":"Thu, 03 Oct 2024 17","Content-Length":"184","X-Cnection":"close","Content-Type":"application/vnd.vimeo.error+json","Via":"1.1 dca1-10"}}
I must have made a very dumb mistake but I'm not very familiar with all those HTTP requests and reponses, does anybody know what I did wrong ?
Thanks !
[edit] Thanks a lot Dashron, I actually had to set jQuery fileupload's multipart option to false :
$('#file').fileupload({
url: upload_link_secure,
type: 'PUT',
multipart: false
});
After that, I was getting this HTTP error :
XMLHttpRequest cannot load https://1511632921.cloud.vimeo.com/upload?[...]. Request header field Content-Disposition is not allowed by Access-Control-Allow-Headers.
There might be a clean fix for that but I didn't find it so I simply commented the lines that set the Content-Disposition header in jquery.fileupload.js
// if (!multipart || options.blob || !this._isInstanceOf('File', file)) {
// options.headers['Content-Disposition'] = 'attachment; filename="' +
// encodeURI(file.name) + '"';
// }
(see edit3)
Now it works fine ! :)
[edit2] I was asked for a more complete example of the code I came up with to make that PUT upload work so here is a Gist containing the relevant Twig template from my Symfony application. I hope it's clear enough and that it can help. The code can probably be improved a lot but I guess it's an okay starting point. https://gist.github.com/paulgv/13ff6d194bc0d662de7b
[edit3] I also realize that I never updated my code with a cleaner fix for the issue I had with the Content-Disposition header (see crossed out text above). Thanks to blueimp's help, I found out that you can simply remove this header in fileuploadsend callback :
.bind('fileuploadsend', function (e, data) {
data.headers = {};
})
PUT uploads do not support multipart form encoding. PUT uploads should have a request body of only the raw bytes of the file.
Multipart is supported on POST uploads, but POST uploads do not support resumable uploads or range headers.
I use Libsyn for several podcasts, and this is a new issue that I've never had before. I publish my own RSS feed, and redirect the audio file requests through my own server so I can do my own logging. iTunes is rejecting my submission of the feed with the message "There is a problem with your feed. Your episode are hosted on a server which doesn't support byte-range requests. Enable byte-range requests and try your submission again."
Obviously, Libsyn DOES support byte-range requests, so the problem appears to be in the way I'm redirecting the requests. This is what has always worked on my existing podcasts:
$id = 12345; // (the episode number)
$url = 'http://traffic.libsyn.com/myshow/myfile-'.$id.'.mp3';
header("Location: $url");
So I've tried adding some headers to convince iTunes that I actually do support byte-range requests. I've tried just about every combination of these that I can think of:
$id = 12345; // (the episode number)
$filesize = 12345678; // (the size of my file on Libsyn)
$url = 'http://traffic.libsyn.com/myshow/myfile-'.$id.'.mp3';
header('HTTP/1.1 206 Partial Content');
header('Content-Type: audio/x-mp3');
header('Accept-Ranges: bytes');
header("Content-Length: $filesize");
header("Location: $url");
I still get the error trying to submit the show to iTunes, and am out of ideas. Any suggestions?
You can't return make a single HTTP response be both a success (206) and a redirect (Location header, implying a 30x). You'll need to either serve the content yourself, or give up on logging.
I encourage the many others who have posted about this question to look for unrelated typos and something silly like my problem was. The error returned by iTunes does not necessarily mean what it says.
With PHP and Apache, is it possible to start the PHP script after the headers are received, but before the body is sent ?
I mean kind of this:
PUT /sync/testStream.php HTTP/1.1
Host: localhost
User-Agent: test
Content-Length: 500
Content-Type: text/plain
<start the script here>
hello
this is a test
The objective is to lock a file during the script (during its upload actually).
I tried to read from php://input, with no success: the script is started only when the whole body is received.
Here is my script:
<?php
echo "Hello. Start sending data.";
ob_flush();
$file = fopen('php://input', 'r');
if ($file === false) {
die("Could not open php://input");
}
while (($line = fgets($file)) !== false) {
echo $line;
}
echo "Bye";
Any hint is welcome !
I have seen this question on here before (can't find it right now, but I have) and after much discussion/debate, that conclusion was that No, you can't.
This is because the file upload process and receiving of the body of the request is handled by the overlying web server, and PHP is not fired up until after the request has been fully received/parsed/validated.
One point here is that your script appears to be attempting to establish full-duplex communication over HTTP, which is not possible. HTTP is by it's very nature a half-duplex protocol - it has a request-response architecture and you cannot start sending a response before you have completely received the request, it would be a protocol violation.
If you explain exactly what you are trying to do/why you want to lock a file during upload, maybe we can find an alternative solution.
Actually, it's not possible because of PHP isn't aware of any request before the HTTP request is completed. In other words, PHP receives the query after the upload is completed.
PHP is executed after whole request.
To achieve this functionality you could write custom apache module.