Related
I want to send an "ajax download request" when I click on a button, so I tried in this way:
javascript:
var xhr = new XMLHttpRequest();
xhr.open("GET", "download.php");
xhr.send();
download.php:
<?
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename= file.txt");
header("Content-Transfer-Encoding: binary");
readfile("file.txt");
?>
but doesn't work as expected, how can I do ? Thank you in advance
Update April 27, 2015
Up and coming to the HTML5 scene is the download attribute. It's supported in Firefox and Chrome, and soon to come to IE11. Depending on your needs, you could use it instead of an AJAX request (or using window.location) so long as the file you want to download is on the same origin as your site.
You could always make the AJAX request/window.location a fallback by using some JavaScript to test if download is supported and if not, switching it to call window.location.
Original answer
You can't have an AJAX request open the download prompt since you physically have to navigate to the file to prompt for download. Instead, you could use a success function to navigate to download.php. This will open the download prompt but won't change the current page.
$.ajax({
url: 'download.php',
type: 'POST',
success: function() {
window.location = 'download.php';
}
});
Even though this answers the question, it's better to just use window.location and avoid the AJAX request entirely.
To make the browser downloads a file you need to make the request like that:
function downloadFile(urlToSend) {
var req = new XMLHttpRequest();
req.open("GET", urlToSend, true);
req.responseType = "blob";
req.onload = function (event) {
var blob = req.response;
var fileName = req.getResponseHeader("fileName") //if you have the fileName header available
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download=fileName;
link.click();
};
req.send();
}
You actually don't need ajax at all for this. If you just set "download.php" as the href on the button, or, if it's not a link use:
window.location = 'download.php';
The browser should recognise the binary download and not load the actual page but just serve the file as a download.
Cross browser solution, tested on Chrome, Firefox, Edge, IE11.
In the DOM, add an hidden link tag:
<a id="target" style="display: none"></a>
Then:
var req = new XMLHttpRequest();
req.open("GET", downloadUrl, true);
req.responseType = "blob";
req.setRequestHeader('my-custom-header', 'custom-value'); // adding some headers (if needed)
req.onload = function (event) {
var blob = req.response;
var fileName = null;
var contentType = req.getResponseHeader("content-type");
// IE/EDGE seems not returning some response header
if (req.getResponseHeader("content-disposition")) {
var contentDisposition = req.getResponseHeader("content-disposition");
fileName = contentDisposition.substring(contentDisposition.indexOf("=")+1);
} else {
fileName = "unnamed." + contentType.substring(contentType.indexOf("/")+1);
}
if (window.navigator.msSaveOrOpenBlob) {
// Internet Explorer
window.navigator.msSaveOrOpenBlob(new Blob([blob], {type: contentType}), fileName);
} else {
var el = document.getElementById("target");
el.href = window.URL.createObjectURL(blob);
el.download = fileName;
el.click();
}
};
req.send();
It is possible. You can have the download started from inside an ajax function, for example, just after the .csv file is created.
I have an ajax function that exports a database of contacts to a .csv file, and just after it finishes, it automatically starts the .csv file download. So, after I get the responseText and everything is Ok, I redirect browser like this:
window.location="download.php?filename=export.csv";
My download.php file looks like this:
<?php
$file = $_GET['filename'];
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=".$file."");
header("Content-Transfer-Encoding: binary");
header("Content-Type: binary/octet-stream");
readfile($file);
?>
There is no page refresh whatsoever and the file automatically starts downloading.
NOTE - Tested in the following browsers:
Chrome v37.0.2062.120
Firefox v32.0.1
Opera v12.17
Internet Explorer v11
I prefer location.assign(url);
Complete syntax example:
document.location.assign('https://www.urltodocument.com/document.pdf');
developer.mozilla.org/en-US/docs/Web/API/Location.assign
For those looking a more modern approach, you can use the fetch API. The following example shows how to download a spreadsheet file. It is easily done with the following code.
fetch(url, {
body: JSON.stringify(data),
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
})
.then(response => response.blob())
.then(response => {
const blob = new Blob([response], {type: 'application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = "file.xlsx";
document.body.appendChild(a);
a.click();
})
I believe this approach to be much easier to understand than other XMLHttpRequest solutions. Also, it has a similar syntax to the jQuery approach, without the need to add any additional libraries.
Of course, I would advise checking to which browser you are developing, since this new approach won't work on IE. You can find the full browser compatibility list on the following link.
Important: In this example I am sending a JSON request to a server listening on the given url. This url must be set, on my example I am assuming you know this part. Also, consider the headers needed for your request to work. Since I am sending a JSON, I must add the Content-Type header and set it to application/json; charset=utf-8, as to let the server know the type of request it will receive.
#Joao Marcos solution works for me but I had to modify the code to make it work on IE, below if what the code looks like
downloadFile(url,filename) {
var that = this;
const extension = url.split('/').pop().split('?')[0].split('.').pop();
var req = new XMLHttpRequest();
req.open("GET", url, true);
req.responseType = "blob";
req.onload = function (event) {
const fileName = `${filename}.${extension}`;
const blob = req.response;
if (window.navigator.msSaveBlob) { // IE
window.navigator.msSaveOrOpenBlob(blob, fileName);
}
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = fileName;
link.click();
URL.revokeObjectURL(link.href);
};
req.send();
},
Decoding a filename from the header is a little bit more complex...
var filename = "default.pdf";
var disposition = req.getResponseHeader('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1)
{
var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
var matches = filenameRegex.exec(disposition);
if (matches != null && matches[1])
filename = matches[1].replace(/['"]/g, '');
}
This solution is not very different from those above, but for me it works very well and i think it's clean.
I suggest to base64 encode the file server side (base64_encode(), if you are using PHP) and send the base64 encoded data to the client
On the client you do this:
let blob = this.dataURItoBlob(THE_MIME_TYPE + "," + response.file);
let uri = URL.createObjectURL(blob);
let link = document.createElement("a");
link.download = THE_FILE_NAME,
link.href = uri;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
This code puts the encoded data in a link and simulates a click on the link, then it removes it.
Your needs are covered by
window.location('download.php');
But I think that you need to pass the file to be downloaded, not always download the same file, and that's why you are using a request, one option is to create a php file as simple as showfile.php and do a request like
var myfile = filetodownload.txt
var url = "shofile.php?file=" + myfile ;
ajaxRequest.open("GET", url, true);
showfile.php
<?php
$file = $_GET["file"]
echo $file;
where file is the file name passed via Get or Post in the request and then catch the response in a function simply
if(ajaxRequest.readyState == 4){
var file = ajaxRequest.responseText;
window.location = 'downfile.php?file=' + file;
}
}
there is another solution to download a web page in ajax. But I am referring to a page that must first be processed and then downloaded.
First you need to separate the page processing from the results download.
1) Only the page calculations are made in the ajax call.
$.post("CalculusPage.php", { calculusFunction: true, ID: 29, data1: "a", data2: "b" },
function(data, status)
{
if (status == "success")
{
/* 2) In the answer the page that uses the previous calculations is downloaded. For example, this can be a page that prints the results of a table calculated in the ajax call. */
window.location.href = DownloadPage.php+"?ID="+29;
}
}
);
// For example: in the CalculusPage.php
if ( !empty($_POST["calculusFunction"]) )
{
$ID = $_POST["ID"];
$query = "INSERT INTO ExamplePage (data1, data2) VALUES ('".$_POST["data1"]."', '".$_POST["data2"]."') WHERE id = ".$ID;
...
}
// For example: in the DownloadPage.php
$ID = $_GET["ID"];
$sede = "SELECT * FROM ExamplePage WHERE id = ".$ID;
...
$filename="Export_Data.xls";
header("Content-Type: application/vnd.ms-excel");
header("Content-Disposition: inline; filename=$filename");
...
I hope this solution can be useful for many, as it was for me.
this works for me
var dataObj = {
somekey:"someValue"
}
$.ajax({
method: "POST",
url: "/someController/someMethod",
data: dataObj,
success: function (response) {
const blob = new Blob([response], { type: 'text/csv' });
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = "file.csv";
document.body.appendChild(a);
a.click();
}
});
The script is working fine, I'm using formData()for uploading the image with ajax, and it is correctly being uploaded to the folder, but I can't understand why my page is being refreshed after move_uploaded_file() gets called in the php script.
This is the full code:
index.html
<label>Add picture?</label>
<input type='file' accept='.png,.gif,.jpg' onchange='upload_img(this)'>
script.js
function upload_img(e) {
let file = e.files[0];
let formData = new FormData();
formData.append('file', file);
let xhr = new XMLHttpRequest();
xhr.onload = function () {
// I want to show a message here but the page is being refreshed
document.getElementById('ajax_result').innerHTML = xhr.responseText;
}
xhr.open("POST", "upload_img.php", true);
xhr.send(formData);
}
upload_img.php
<?php
if (!empty($_FILES['file'])) {
var_dump($_FILES['file']);
$path = "../img_uploads/";
$path = $path . basename($_FILES['file']['name']);
if (move_uploaded_file($_FILES['file']['tmp_name'], $path)) {
echo "The file " . basename($_FILES['file']['name']) . " has been uploaded";
}
}
EDIT:
For testing purposes I deleted all forms in the page and the issue is still there.
Maybe try first xfr.open then xfr.onload
I have a PHP code which produces a zip file and make it downloadable from browser. The download part looks like:
download.php
// force client download
if (headers_sent()) {
echo 'HTTP header already sent';
} else {
if (!is_file($zipFile)) {
header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found');
echo $zipFile . ' not found';
} else if (!is_readable($zipFile)) {
header($_SERVER['SERVER_PROTOCOL'].' 403 Forbidden');
echo $zipFile . ' not readable';
} else {
ob_start();
// http headers for zip downloads
header('Content-Description: File Transfer');
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="' . $zipName . '"');
header('Content-Transfer-Encoding: binary');
header('Connection: Keep-Alive');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
set_time_limit(0);
ob_flush();
ob_clean();
readfile($zipFile);
}
}
When called from the browser directly like localhost/download.php it works and makes me download the zipped file properly.
However, I need to call it from my JS web application.
The way I am invoking it is via a POST AJAX request that look like this:
var xhr;
if (window.XMLHttpRequest) xhr = new XMLHttpRequest(); // all browsers
else xhr = new ActiveXObject("Microsoft.XMLHTTP"); // for IE
var php_url = '/localhost/download.php' // ?wfs_url=' + url + 'format=' + format_list[0];
// (https://stackoverflow.com/a/53982364/1979665)
var formData = new FormData();
formData.append('wfs_url', url);
formData.append('format', format_list[0]);
xhr.open('POST', php_url);
xhr.onreadystatechange = function () {
if (xhr.readyState===4 && xhr.status===200) {
alert('Server reply: ' + xhr.responseText);
}
}
xhr.send(formData);
return false;
The code is triggered when I click on a button.
Apparently it's doing "something". The PHP intermediate outputs (some folders) are correctly created, but the download does not begin.
The alert('Server reply: ' + xhr.responseText); part shows a strange message with some messy symbols, which I guess derives from the files created and read as text somehow.
Here is a screenshot of the message:
The PHP script is sending the actual binary source of the zip file, while the AJAX request is trying to display it as text, that's the garbage you get.
If you really want to keep the AJAX (it would be more simple to just make a <form> that is sent to download.php), you could do one two things:
Just print the URL to $zipFile in PHP, and then when you get the response redirect to it with window.location.href=xhr.responseText;.
Save the response as a zip file. See How to save binary data of zip file in Javascript?
After following option 2 in #Gabriel accepted answer I was able to make my code working. Here is the modified version of the JS (I didn't make any change in the PHP code):
var php_url = '/localhost/download.php' // ?wfs_url=' + url + 'format=' + format_list[0];
// (https://stackoverflow.com/a/53982364/1979665)
var formData = new FormData();
formData.append('wfs_url', url);
formData.append('format', format_list[0]);
xhr.open('POST', php_url);
xhr.onreadystatechange = function () {
if (xhr.readyState===4 && xhr.status===200) {
var blob = new Blob([xhr.response], { // SOLUTION
type: "application/zip", // SOLUTION
}); // SOLUTION
var filename = "test.zip"; // SOLUTION
if (navigator.msSaveOrOpenBlob) { // SOLUTION
navigator.msSaveOrOpenBlob(blob, filename); // SOLUTION
} else { // SOLUTION
var a = document.createElement("a"); // SOLUTION
document.body.appendChild(a); // SOLUTION
a.style = "display:none"; // SOLUTION
var url = window.URL.createObjectURL(blob); // SOLUTION
a.href = url; // SOLUTION
a.download = filename; // SOLUTION
a.click(); // SOLUTION
window.URL.revokeObjectURL(url); // SOLUTION
a.remove(); // SOLUTION
} // SOLUTION
}
}
xhr.responseType = "arraybuffer"; // SOLUTION
xhr.send(formData);
I want to send an "ajax download request" when I click on a button, so I tried in this way:
javascript:
var xhr = new XMLHttpRequest();
xhr.open("GET", "download.php");
xhr.send();
download.php:
<?
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename= file.txt");
header("Content-Transfer-Encoding: binary");
readfile("file.txt");
?>
but doesn't work as expected, how can I do ? Thank you in advance
Update April 27, 2015
Up and coming to the HTML5 scene is the download attribute. It's supported in Firefox and Chrome, and soon to come to IE11. Depending on your needs, you could use it instead of an AJAX request (or using window.location) so long as the file you want to download is on the same origin as your site.
You could always make the AJAX request/window.location a fallback by using some JavaScript to test if download is supported and if not, switching it to call window.location.
Original answer
You can't have an AJAX request open the download prompt since you physically have to navigate to the file to prompt for download. Instead, you could use a success function to navigate to download.php. This will open the download prompt but won't change the current page.
$.ajax({
url: 'download.php',
type: 'POST',
success: function() {
window.location = 'download.php';
}
});
Even though this answers the question, it's better to just use window.location and avoid the AJAX request entirely.
To make the browser downloads a file you need to make the request like that:
function downloadFile(urlToSend) {
var req = new XMLHttpRequest();
req.open("GET", urlToSend, true);
req.responseType = "blob";
req.onload = function (event) {
var blob = req.response;
var fileName = req.getResponseHeader("fileName") //if you have the fileName header available
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download=fileName;
link.click();
};
req.send();
}
You actually don't need ajax at all for this. If you just set "download.php" as the href on the button, or, if it's not a link use:
window.location = 'download.php';
The browser should recognise the binary download and not load the actual page but just serve the file as a download.
Cross browser solution, tested on Chrome, Firefox, Edge, IE11.
In the DOM, add an hidden link tag:
<a id="target" style="display: none"></a>
Then:
var req = new XMLHttpRequest();
req.open("GET", downloadUrl, true);
req.responseType = "blob";
req.setRequestHeader('my-custom-header', 'custom-value'); // adding some headers (if needed)
req.onload = function (event) {
var blob = req.response;
var fileName = null;
var contentType = req.getResponseHeader("content-type");
// IE/EDGE seems not returning some response header
if (req.getResponseHeader("content-disposition")) {
var contentDisposition = req.getResponseHeader("content-disposition");
fileName = contentDisposition.substring(contentDisposition.indexOf("=")+1);
} else {
fileName = "unnamed." + contentType.substring(contentType.indexOf("/")+1);
}
if (window.navigator.msSaveOrOpenBlob) {
// Internet Explorer
window.navigator.msSaveOrOpenBlob(new Blob([blob], {type: contentType}), fileName);
} else {
var el = document.getElementById("target");
el.href = window.URL.createObjectURL(blob);
el.download = fileName;
el.click();
}
};
req.send();
It is possible. You can have the download started from inside an ajax function, for example, just after the .csv file is created.
I have an ajax function that exports a database of contacts to a .csv file, and just after it finishes, it automatically starts the .csv file download. So, after I get the responseText and everything is Ok, I redirect browser like this:
window.location="download.php?filename=export.csv";
My download.php file looks like this:
<?php
$file = $_GET['filename'];
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=".$file."");
header("Content-Transfer-Encoding: binary");
header("Content-Type: binary/octet-stream");
readfile($file);
?>
There is no page refresh whatsoever and the file automatically starts downloading.
NOTE - Tested in the following browsers:
Chrome v37.0.2062.120
Firefox v32.0.1
Opera v12.17
Internet Explorer v11
I prefer location.assign(url);
Complete syntax example:
document.location.assign('https://www.urltodocument.com/document.pdf');
developer.mozilla.org/en-US/docs/Web/API/Location.assign
For those looking a more modern approach, you can use the fetch API. The following example shows how to download a spreadsheet file. It is easily done with the following code.
fetch(url, {
body: JSON.stringify(data),
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
})
.then(response => response.blob())
.then(response => {
const blob = new Blob([response], {type: 'application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = "file.xlsx";
document.body.appendChild(a);
a.click();
})
I believe this approach to be much easier to understand than other XMLHttpRequest solutions. Also, it has a similar syntax to the jQuery approach, without the need to add any additional libraries.
Of course, I would advise checking to which browser you are developing, since this new approach won't work on IE. You can find the full browser compatibility list on the following link.
Important: In this example I am sending a JSON request to a server listening on the given url. This url must be set, on my example I am assuming you know this part. Also, consider the headers needed for your request to work. Since I am sending a JSON, I must add the Content-Type header and set it to application/json; charset=utf-8, as to let the server know the type of request it will receive.
#Joao Marcos solution works for me but I had to modify the code to make it work on IE, below if what the code looks like
downloadFile(url,filename) {
var that = this;
const extension = url.split('/').pop().split('?')[0].split('.').pop();
var req = new XMLHttpRequest();
req.open("GET", url, true);
req.responseType = "blob";
req.onload = function (event) {
const fileName = `${filename}.${extension}`;
const blob = req.response;
if (window.navigator.msSaveBlob) { // IE
window.navigator.msSaveOrOpenBlob(blob, fileName);
}
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = fileName;
link.click();
URL.revokeObjectURL(link.href);
};
req.send();
},
Decoding a filename from the header is a little bit more complex...
var filename = "default.pdf";
var disposition = req.getResponseHeader('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1)
{
var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
var matches = filenameRegex.exec(disposition);
if (matches != null && matches[1])
filename = matches[1].replace(/['"]/g, '');
}
This solution is not very different from those above, but for me it works very well and i think it's clean.
I suggest to base64 encode the file server side (base64_encode(), if you are using PHP) and send the base64 encoded data to the client
On the client you do this:
let blob = this.dataURItoBlob(THE_MIME_TYPE + "," + response.file);
let uri = URL.createObjectURL(blob);
let link = document.createElement("a");
link.download = THE_FILE_NAME,
link.href = uri;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
This code puts the encoded data in a link and simulates a click on the link, then it removes it.
Your needs are covered by
window.location('download.php');
But I think that you need to pass the file to be downloaded, not always download the same file, and that's why you are using a request, one option is to create a php file as simple as showfile.php and do a request like
var myfile = filetodownload.txt
var url = "shofile.php?file=" + myfile ;
ajaxRequest.open("GET", url, true);
showfile.php
<?php
$file = $_GET["file"]
echo $file;
where file is the file name passed via Get or Post in the request and then catch the response in a function simply
if(ajaxRequest.readyState == 4){
var file = ajaxRequest.responseText;
window.location = 'downfile.php?file=' + file;
}
}
there is another solution to download a web page in ajax. But I am referring to a page that must first be processed and then downloaded.
First you need to separate the page processing from the results download.
1) Only the page calculations are made in the ajax call.
$.post("CalculusPage.php", { calculusFunction: true, ID: 29, data1: "a", data2: "b" },
function(data, status)
{
if (status == "success")
{
/* 2) In the answer the page that uses the previous calculations is downloaded. For example, this can be a page that prints the results of a table calculated in the ajax call. */
window.location.href = DownloadPage.php+"?ID="+29;
}
}
);
// For example: in the CalculusPage.php
if ( !empty($_POST["calculusFunction"]) )
{
$ID = $_POST["ID"];
$query = "INSERT INTO ExamplePage (data1, data2) VALUES ('".$_POST["data1"]."', '".$_POST["data2"]."') WHERE id = ".$ID;
...
}
// For example: in the DownloadPage.php
$ID = $_GET["ID"];
$sede = "SELECT * FROM ExamplePage WHERE id = ".$ID;
...
$filename="Export_Data.xls";
header("Content-Type: application/vnd.ms-excel");
header("Content-Disposition: inline; filename=$filename");
...
I hope this solution can be useful for many, as it was for me.
this works for me
var dataObj = {
somekey:"someValue"
}
$.ajax({
method: "POST",
url: "/someController/someMethod",
data: dataObj,
success: function (response) {
const blob = new Blob([response], { type: 'text/csv' });
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = "file.csv";
document.body.appendChild(a);
a.click();
}
});
Trying to Upload some Images stored in Filesystem of Google Chrome.
But Im not able to upload the Image. Any Idea how to do it?
The Server receives an empty array. The Code of posttest.php is just print_r($_POST)
var xhr = new XMLHttpRequest();
xhr.open('POST', '/posttest.php', true);
xhr.onload = function(e) {
if (this.status == 200) {
console.log(this.responseText);
}
};
window.resolveLocalFileSystemURL(image, function(fileEntry) {
fileEntry.file(function(file) {
var reader = new FileReader();
reader.onloadend = function(e) {
var formData = new FormData();
formData.append('image', this.result);
xhr.send(formData);
};
reader.readAsText(file);
});
});
this is the Javascript function that worked for me in chrome
function upload(filename) {
var xhr = new XMLHttpRequest();
xhr.open("post", "upload.php", true);
window.resolveLocalFileSystemURL = window.resolveLocalFileSystemURL || window.webkitResolveLocalFileSystemURL;
filename = 'filesystem:http://localhost/temporary/' + filename;
window.resolveLocalFileSystemURL(filename, function(fileEntry) {
fileEntry.file(function(file) {
xhr.setRequestHeader("Content-Type", "multipart/form-data");
xhr.setRequestHeader("X-File-Name", file.name);
xhr.setRequestHeader("X-File-Size", file.size);
xhr.setRequestHeader("X-File-Type", file.type);
xhr.send(file);
});
});
}
Now most people on the skip the upload.php taking it for granted. But it is very important and so I paste it here:
<?php
// I had to figure out what is getting passed using var_dump($_SERVER)
$fn = (isset($_SERVER['HTTP_X_FILE_NAME']) ? $_SERVER['HTTP_X_FILE_NAME'] : false);
if ($fn) {
// AJAX call
file_put_contents(
'uploads/' . $fn,
file_get_contents('php://input')
);
echo "$fn uploaded";
exit();
}
?>
Hope this helps someone, I wasted an entire day to figure this out !
First of all, your comment isn't exactly correct, the PHP code shouldn't even be receiving a blob. It's just receiving text, because you read the file as text, while png images (and almost all image types) aren't supposed to be text, they're supposed to be binary. And Blobs themselves are perfect for storing binary data, so you don't even need to use a FileReader, you can just send the Blob itself through XMLHttpRequest!
Here is how the revised fileEntry.file callback function should look:
fileEntry.file(function(file) {
xhr.send(file);
});
It's that simple! No need to read the Blob. However, you need to do some more stuff on the
PHP side now. This code will write the contents of the uploaded image to disk, then create an <img> element that allows you to view it. Here's how the PHP /posttest.php should look for that:
<?php
$rhand=fopen('php://input','r');
$cont=fread($rhand,filesize('php://input'));
fclose($rhand);
$whand=fopen('./uploaded.png','w');
fwrite($whand,$cont);
fclose($whand);
?>
<img src="uploaded.png">
It should be pretty clear what that does if you know that php://input is where php will get input from a post request if it doesn't have a MIME type that allows it to easily put it into $_POST (For example, when you have have a request with MIME type application/x-www-form-urlencoded, PHP recognizes it and is able to parse the POST contents into $_POST, but with a Blob, it doesn't, so it just outputs it out of php://input)
Please note that none of this is tested, please put a comment if it doesn't work and I'll try to fix it!
Having encountered the same challenge myself, I was able to come up with a very sleek solution. The key is to create a new Blob, from the file object returned by the fileEntry.
window.resolveLocalFileSystemURL = window.resolveLocalFileSystemURL || window.webkitResolveLocalFileSystemURL;
window.resolveLocalFileSystemURL(image, function (fileEntry) {
fileEntry.file(function (file) {
var formData = new FormData();
var tempBlob=new Blob([this.result], {type:this.result.type});
formData.append('image', tempBlob);
});
});