letronje's solution only works for very simple pages. document.body.innerHTML +=
takes the HTML text of the body, appends the iframe HTML, and sets the innerHTML of the page to that string. This will wipe out any event bindings your page has, amongst other things. Create an element and use appendChild
instead.
$.post('/create_binary_file.php', postData, function(retData) {
var iframe = document.createElement("iframe");
iframe.setAttribute("src", retData.url);
iframe.setAttribute("style", "display: none");
document.body.appendChild(iframe);
});
Or using jQuery
$.post('/create_binary_file.php', postData, function(retData) {
$("body").append("<iframe src='" + retData.url+ "' style='display: none;' ></iframe>");
});
What this actually does: perform a post to /create_binary_file.php with the data in the variable postData; if that post completes successfully, add a new iframe to the body of the page. The assumption is that the response from /create_binary_file.php will include a value 'url', which is the URL that the generated PDF/XLS/etc file can be downloaded from. Adding an iframe to the page that references that URL will result in the browser promoting the user to download the file, assuming that the web server has the appropriate mime type configuration.
I've been playing around with another option that uses blobs. I've managed to get it to download text documents, and I've downloaded PDF's (However they are corrupted).
Using the blob API you will be able to do the following:
$.post(/*...*/,function (result)
{
var blob=new Blob([result]);
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="myFileName.txt";
link.click();
});
This is IE 10+, Chrome 8+, FF 4+. See https://developer.mozilla.org/en-US/docs/Web/API/URL.createObjectURL
It will only download the file in Chrome, Firefox and Opera. This uses a download attribute on the anchor tag to force the browser to download it.
I know this kind of old, but I think I have come up with a more elegant solution. I had the exact same problem. The issue I was having with the solutions suggested were that they all required the file being saved on the server, but I did not want to save the files on the server, because it introduced other problems (security: the file could then be accessed by non-authenticated users, cleanup: how and when do you get rid of the files). And like you, my data was complex, nested JSON objects that would be hard to put into a form.
What I did was create two server functions. The first validated the data. If there was an error, it would be returned. If it was not an error, I returned all of the parameters serialized/encoded as a base64 string. Then, on the client, I have a form that has only one hidden input and posts to a second server function. I set the hidden input to the base64 string and submit the format. The second server function decodes/deserializes the parameters and generates the file. The form could submit to a new window or an iframe on the page and the file will open up.
There's a little bit more work involved, and perhaps a little bit more processing, but overall, I felt much better with this solution.
Code is in C#/MVC
public JsonResult Validate(int reportId, string format, ReportParamModel[] parameters)
{
// TODO: do validation
if (valid)
{
GenerateParams generateParams = new GenerateParams(reportId, format, parameters);
string data = new EntityBase64Converter<GenerateParams>().ToBase64(generateParams);
return Json(new { State = "Success", Data = data });
}
return Json(new { State = "Error", Data = "Error message" });
}
public ActionResult Generate(string data)
{
GenerateParams generateParams = new EntityBase64Converter<GenerateParams>().ToEntity(data);
// TODO: Generate file
return File(bytes, mimeType);
}
on the client
function generate(reportId, format, parameters)
{
var data = {
reportId: reportId,
format: format,
params: params
};
$.ajax(
{
url: "/Validate",
type: 'POST',
data: JSON.stringify(data),
dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: generateComplete
});
}
function generateComplete(result)
{
if (result.State == "Success")
{
// this could/should already be set in the HTML
formGenerate.action = "/Generate";
formGenerate.target = iframeFile;
hidData = result.Data;
formGenerate.submit();
}
else
// TODO: display error messages
}
There is a simplier way, create a form and post it, this runs the risk of resetting the page if the return mime type is something that a browser would open, but for csv and such it's perfect
Example requires underscore and jquery
var postData = {
filename:filename,
filecontent:filecontent
};
var fakeFormHtmlFragment = "<form style='display: none;' method='POST' action='"+SAVEAS_PHP_MODE_URL+"'>";
_.each(postData, function(postValue, postKey){
var escapedKey = postKey.replace("\\", "\\\\").replace("'", "\'");
var escapedValue = postValue.replace("\\", "\\\\").replace("'", "\'");
fakeFormHtmlFragment += "<input type='hidden' name='"+escapedKey+"' value='"+escapedValue+"'>";
});
fakeFormHtmlFragment += "</form>";
$fakeFormDom = $(fakeFormHtmlFragment);
$("body").append($fakeFormDom);
$fakeFormDom.submit();
For things like html, text and such, make sure the mimetype is some thing like application/octet-stream
php code
<?php
/**
* get HTTP POST variable which is a string ?foo=bar
* @param string $param
* @param bool $required
* @return string
*/
function getHTTPPostString ($param, $required = false) {
if(!isset($_POST[$param])) {
if($required) {
echo "required POST param '$param' missing";
exit 1;
} else {
return "";
}
}
return trim($_POST[$param]);
}
$filename = getHTTPPostString("filename", true);
$filecontent = getHTTPPostString("filecontent", true);
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"$filename\"");
echo $filecontent;
In short, there is no simpler way. You need to make another server request to show PDF file. Al though, there are few alternatives but they are not perfect and won't work on all browsers:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With