使用ajax请求在浏览器中下载
Posted
技术标签:
【中文标题】使用ajax请求在浏览器中下载【英文标题】:Download in browser with ajax request 【发布时间】:2015-08-26 22:33:13 【问题描述】:我正在使用 AngularJS,我正在尝试在 php 中生成 PDF。这就是我的 controller.js 中的内容:
$scope.downloadPDF = function()
$http.post('/download-pdf', fid: $routeParams.fid )
.success(function(data, status, headers, config)
console.log("success");
)
.error(function(data, status, headers, config)
console.log("error");
);
;
在我的 php 文件中,我有以下内容可以使用FPDF library 创建 PDF:
function download_pdf()
$id = $_POST['fid'];
$pdf = new FPDF();
$pdf->AddPage();
$pdf->SetFont('Arial','B',16);
$pdf->Cell(40,10,'Hello World!');
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename=' . $id . '.pdf');
$pdf->Output('Order123.pdf', 'D');
但是请求是响应这个而不是打开保存对话框来保存我的 pdf。
%PDF-1.3 3 0 对象 结束对象 4 0 对象 溪流 x3Rðâ2Ð35W(çr QÐw3T04Ó30PISp êZ*[¤(hx¤æää+çå¤(j*dÔ7W 端流 结束对象 1 0 对象 /X 对象 > 结束对象 6 0 对象 结束对象 7 0 对象 结束对象 外部参照 0 8 0000000000 65535 f 0000000228 00000 0000000416 00000 0000000009 00000 n 0000000087 00000 0000000315 00000 0000000520 00000 0000000595 00000 预告片 起始外部参照 644 %%EOF
我已经使用了PHPExcel 库,这很有效:
$objWriter = PHPExcel_IOFactory::createWriter($ea, 'Excel2007');
// We'll be outputting an excel file
header('Content-type: application/vnd.ms-excel');
// It will be called Submission on [date_now].xls
header('Content-Disposition: attachment; filename="' . $filename . '.xls' . '"');
// Write file to the browser
$objWriter->save('php://output');
现在我怎样才能使它适用于我的 PDF?
更新:
我已将我的代码编辑为:
$pdf = new FPDF();
$pdf->AddPage();
$pdf->SetFont('Arial','B',16);
$pdf->Cell(40,10,'Hello World!');
$filename = DXS_VKGROUP_PLUGIN_LIB_DIR . 'uploads/' . $_POST['fid'] . '.pdf';
$pdf->Output($filename, 'F'); // Save file locally
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Content-Type: application-download');
header('Content-Length: ' . filesize($filename));
header('Content-Transfer-Encoding: binary');
header('Content-Disposition: attachment; filename="' . $filename . '"');
$handle = fopen($filename, 'rb');
fpassthru($handle);
fclose($handle);
文件保存在本地,但无法下载。它没有让我的对话框保存 pdf。我做错了什么?
更新 2
我现在尝试将application-download
更改为application/pdf
。他将文件保存在本地,但我没有得到下载对话框。
响应看起来像这样(当我在 Chrome 中检查 Network 时):
%PDF-1.3 3 0 对象 结束对象 4 0 对象 溪流 x3Rðâ2Ð35W(çr QÐw3T04Ó30PISp êZ*[¤(hx¤æää+çå¤(j*dÔ7W 端流 结束对象 1 0 对象 /X 对象 > 结束对象 6 0 对象 结束对象 7 0 对象 结束对象 外部参照 0 8 0000000000 65535 f 0000000228 00000 0000000416 00000 0000000009 00000 n 0000000087 00000 0000000315 00000 0000000520 00000 0000000595 00000 预告片 起始外部参照 644 %%EOF
【问题讨论】:
如果您的网络浏览器设置没有勾选“下载前询问每个文件的保存位置”(Chrome)选项或类似选项(Firefox - 始终询问保存文件的位置),那么浏览器将不会即使您将文件的标题设置为application-download
或 Content-Disposition: attachment
,也不会显示“另存为”对话框。该文件将自动开始下载。在这种情况下,github.com/eligrey/FileSaver.js 之类的东西可能会有所帮助
如果你使用header('Content-Type: application/octet-stream');
而不是application/pdf
会发生什么?
@Darren 我检查了,这也导致文件下载过程自动启动而没有任何保存提示
您尝试了吗:this 或 this?
你为什么让赏金到期?没有一个答案有用,还是...?
【参考方案1】:
我是按以下方式完成的,它对我很有效:
$(document).ready(function ()
$('#generaPdf').click(function (e)
e.preventDefault();
var name = $('#name').val();
$.ajax
(
type: "POST",
url: "pdf.php",
data: "name": name ,
success: function (data)
alert("todo ok")
location.href = "pdf.php"
// $('.result').html(data);
// $('#contactform')[0].reset();
);
);
);
我的html:
<button type="submit" class="btn btn-primary" id="generaPdf"><i class="fas fa-link"></i> Generar documento para impresión</button>
【讨论】:
【参考方案2】:让我建议你一件事。
仅通过 AJAX 您无法下载任何文件。 您需要先使用 ajax 调用创建 .zip 文件,然后获得该文件路径,然后您必须使用任何命令打开该文件。
这里有一个对我有用的例子:)
$http.post('www.abc.com/zipcreate', 'id': id).then(function (zipurl)
$window.location = zipurl;
);
当然,它有效。试一次。 :)
编辑:
或者你可以使用
$window.location = 'abc.com/link';
在“abc.com/link”中: 使用下面的代码:
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=".$name);
header("Pragma: no-cache");
header("Expires: 0");
readfile($file);
exit;
您将在下载列表中获得您的文件。
【讨论】:
他们为什么需要创建一个 zip 文件?这不会让用户更难实际打开文档吗?【参考方案3】:这对于(仅)AJAX 是不可能的。
您可以在此处使用两种主要技术,均不使用使用 javascript 来执行实际下载。一般的想法是您将浏览器重定向到将提供所需文档的资源;如果您要重定向到下载,它不会离开页面,而是显示“保存”对话框。 this question 中讨论了类似的主题。
GET 请求
location.href = '/download-pdf?fid=' + encodeURIComponent($routeParams.fid);
调整您的服务器端脚本以使用$_GET['fid']
。
POST 请求
服务器创建一个资源并返回一个可以找到该资源的 URL:
$http.post('/download-pdf', fid: $routeParams.fid )
.success(function(data, status, headers, config)
// follow location provided by server
location.href = data.redirect_url;
console.log("success");
)
.error(function(data, status, headers, config)
console.log("error");
);
调整您的服务器端脚本以在创建新资源后返回适当的 JSON 响应。
【讨论】:
【参考方案4】:更新
使用 FileSaver.js 的方法也不起作用,似乎没有办法仅通过 JavaScript 强制调用本机另存为对话框,唯一的例外是 IE 的 saveAs execCommand
。检查Does execCommand SaveAs work in Firefox?
在你的项目中包含FileSaver.js作为依赖项
修改downloadPDF
函数如下
$scope.downloadPDF = function()
$http.post('/download-pdf',
fid: $routeParams.fid
,
responseType: 'arraybuffer'
)
.success(function(data, status, headers, config)
// Convert response data to Blob
var file = new Blob([data],
type: 'application/pdf'
);
// Call the File Saver method to save the above Blob,this will show Save As dialog
saveAs(file, "test.pdf");
)
.error(function(data, status, headers, config)
console.log("error");
);
;
Blob
对象可以在大多数现代浏览器中工作,但对 IE https://github.com/eligrey/FileSaver.js#supported-browsers 以获取 polyfills
同时删除 Content-Disposition: attachment
和 Content-Type: application-download
标头,因为我们不希望浏览器原生处理下载过程
这是一个工作演示 http://plnkr.co/edit/9r1tehQ94t5vkqZfZuQC?p=preview,它显示了对 PDF 的 GET 请求
【讨论】:
试了demo:Chrome没有另存为,直接下载即可 这个答案只是提到了什么不起作用,为什么它仍然得到这么多的赞成票?【参考方案5】:将文档发送到给定的目的地:浏览器、文件或字符串。在浏览器的情况下,可以使用插件(如果存在)或强制下载(“另存为”对话框)。
如果需要终止文档,该方法首先调用 Close()。
参数
姓名:
文件的名称。如果未指定,文档将被发送到名为 doc.pdf 的浏览器(目标 I)。
目的地
发送文档的目的地。它可以采用以下值之一:
我: 将文件内联发送到浏览器。如果使用该插件 可用的。选择“保存”时使用由名称给出的名称 as”选项在生成 PDF 的链接上。 D: 发送到浏览器并强制下载指定名称的文件 按名字。 F: 以 name 给出的名称保存到本地文件(可能 包括路径)。 S: 将文档作为字符串返回。名称被忽略。【讨论】:
【参考方案6】:header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"" . basename($file_url) . "\"");
或使用 BLOB:
$scope.downloadPDF = function()
$http.post('/download-pdf',
fid: $routeParams.fid
,
responseType: 'arraybuffer'
)
.success(function(data, status, headers, config)
var file = new Blob([data],
type: 'application/pdf'
);
var url = URL.createObjectURL(blob);
window.location.href = url;
)
.error(function(data, status, headers, config)
console.log("error");
);
;
或者创建一个带有download
属性的元素:
$scope.downloadPDF = function()
$http.get('http://46.101.139.188/demo.pdf',
,
responseType: 'arraybuffer'
)
.success(function(data, status, headers, config)
var file = new Blob([data],
type: 'application/pdf'
);
var url = URL.createObjectURL(file);
var a = document.createElement('a');
a.setAttribute('download', 'download');
a.setAttribute('href', url);
var event = new MouseEvent('click',
'view': window,
'bubbles': true,
'cancelable': true
);
a.dispatchEvent(event);
)
.error(function(data, status, headers, config)
console.log("error");
);
;
plnkr demo
【讨论】:
对于大的 PDF 文档,URL 会不会太长?你测试了吗?【参考方案7】:使用 ajax 是不可能的。 相反,您可以做的是通过 javascript 表单提交来执行您的操作。
例如,document.getElementById(formID).action= actionName
。
document.getElementById(formID).submit();
这里的 formID 是您的表单 ID,actionName
将是您指定的 '/download-pdf'
。
在您的操作类中 - 设置响应标头
getResponse().setContentType("application/pdf");
getResponse().setHeader("Content-Disposition", " attachment; filename= "+"pdffile.pdf");
这将导致您的文件以 filename = pdffile.pdf 下载。
【讨论】:
【参考方案8】:根据documentation可以设置第二个参数为D
:
$pdf->Output('Order123.pdf', 'D');
如果您想使用保存的文件自己处理所有细节,这就是我将 PDF 呈现给浏览器以从 PHP 下载的方式:
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Content-Type: application-download');
header('Content-Length: ' . filesize('file.pdf'));
header('Content-Transfer-Encoding: binary');
header('Content-Disposition: attachment; filename="file.pdf"');
$handle = fopen('file.pdf', 'rb');
fpassthru($handle);
fclose($handle);
更新:
请确认 PDF 文件确实正在制作中。 Web 服务器进程是否对该路径具有写入权限? FPDF 是否被正确包含?我在一个测试虚拟机上做了这个,它提供了一个文件1.pdf
供下载,它打开并包含“Hello World!”:
<?php
require('/var/www/html/fpdf17/fpdf.php');
$_POST = array('fid' => '1');
$pdf = new FPDF();
$pdf->AddPage();
$pdf->SetFont('Arial', 'B', 16);
$pdf->Cell(40, 10, 'Hello World!');
$filename = '/tmp/' . $_POST['fid'] . '.pdf';
$pdf->Output($filename, 'F'); // Save file locally
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Content-Type: application-download');
header('Content-Length: ' . filesize($filename));
header('Content-Transfer-Encoding: binary');
header('Content-Disposition: attachment; filename="' . basename($filename) . '"');
$handle = fopen($filename, 'rb');
fpassthru($handle);
fclose($handle);
unlink($filename);
?>
【讨论】:
我试过D参数,我的题目错了。但仍然输出与主题中所述相同的内容。 在这种情况下,尝试使用F
参数保存本地文件,然后按照我展示的方式将该文件传递给浏览器。最后您可以使用unlink($filename);
将其删除。
尝试header('Content-Disposition: attachment; filename="' . basename($filename) . '"');
,因为您不能使用具有完整文件夹路径的文件名。您是否在该代码之前向浏览器发送任何标头?
还是一样的结果。不,我之前没有向浏览器发送任何标题.. 在 firefox/chrome 中尝试过
文件确实被保存了,我可以看到并打开它(当我没有取消链接时)。但是浏览器没有打开下载对话框... .以上是关于使用ajax请求在浏览器中下载的主要内容,如果未能解决你的问题,请参考以下文章