onvno / pokerface

日常技术文章阅读整理
3 stars 0 forks source link

20190401 - POST请求下载文件 #12

Open onvno opened 5 years ago

onvno commented 5 years ago

http://blog.fedepot.com/postti-jiao-fang-shi-xia-zai-wen-jian/ 提要 通常下载文件是直接GET访问下载链接,由浏览器默认行为进行下载。对于一些动态生成数据的场景,特别是需要用户提交部分参数,这时候GET请求就不合适了,需要POST提交进行下载。

Form Post下载文件 这是浏览器最原生的表单提交,可以设定form元素的method为POST,这样请求格式为application/x-www-form-urlencoded,如果响应为文档流,浏览器会自动弹出下载对话框。这种方式的兼容性最好,但是页面会跳转是提交的action页面,或者设定form的target为_blank来新窗口/标签弹出下载对话框。

利用Blob新API 现代浏览器中已经新增了很多文件、流处理的API,这里我们需要利用Blob对象完成对文件流的接收。

function download() {  
    const defaultFilename = "unknownfile";
    return fetch("http://127.0.0.1:9000/download", {
        method: "POST",
        credentials: "include",
        headers: {
            "Content-Type": "application/json"
        }
    }).then(resp => {
        return resp.blob().then(blob => {
            // handle blob object
        });
    });
}

收到数据响应并完成文件接收之后,需要调出浏览器的下载行为,这里可以伪造一个a元素,绑定blob对象,并执行元素锚点点击,接下来则由浏览器默认行为接管下载。

function download() {  
    const defaultFilename = "unknownfile";
    return fetch("http://127.0.0.1:9000/download", {
        method: "POST",
        credentials: "include",
        headers: {
            "Content-Type": "application/json"
        }
    }).then(resp => {
        return resp.blob().then(blob => {
            const disposition = resp.headers.get("content-disposition");
            const match = disposition ? disposition.match(/attachment; filename="(.+)"/i) : null;
            const filename = match && match.length > 1 ? match[1] : defaultFilename;
            let a = document.createElement("a");
            document.body.appendChild(a);
            let url = window.URL.createObjectURL(blob);
            a.href = url;
            a.download = filename;
            a.click();
            window.URL.revokeObjectURL(url);
        });
    });
}

衍生-前台伪下载 有了第二种方法,我们还可以衍生它的应用。例如用户前台填写了一些数据,想不通过后台交互,完成提供用户下载数据的功能。下面举个例子用户填写了数据,下载为csv文件。

function fakeDownload() {  
    const records = [
        {
            key: "key1",
            value: "value1"
        },
        {
            key: "key2",
            value: "value2"
        }
    ]
    let data =
        "col1,col2";
    records.forEach(record => {
        data += `\n${record.key},${record.value}`;
    });
    const blob = new Blob([data], { type: "text/csv;charset=utf-8" });
    const link = document.createElement("a");
    let url = window.URL.createObjectURL(blob);
    link.setAttribute("href", url);
    link.setAttribute("download", "export.csv");
    document.body.appendChild(link);
    link.click();
    window.URL.revokeObjectURL(url);
}
onvno commented 5 years ago

Downloading files from Ajax POST Requests 使用了上边第二种方式

onvno commented 5 years ago

原文:https://blog.csdn.net/Sunny__wei/article/details/70214103 post请求无法直接发送请求下载excel文档,是因为我们在后台改变了响应头的内容: Content-Type: application/vnd.ms-excel 导致post请求无法识别这种消息头,导致无法直接下载。

解决方法: 改成使用form表单提交方式即可

/*===================post请求下载文件
 * options:{
 * url:'',  //下载地址
 * data:{name:value}, //要发送的数据
 * method:'post'
 * }
 */
var postDownLoadFile = function (options) {
    var config = $.extend(true, { method: 'post' }, options);
    var $iframe = $('<iframe id="down-file-iframe" />');
    var $form = $('<form target="down-file-iframe" method="' + config.method + '" />');
    $form.attr('action', config.url);
    for (var key in config.data) {
        $form.append('<input type="hidden" name="' + key + '" value="' + config.data[key] + '" />');
    }
    $iframe.append($form);
    $(document.body).append($iframe);
    $form[0].submit();
    $iframe.remove();
}

//导出
$("#btnExport_year").on('click',
        function() {
          var param={};
          postDownLoadFile({
            url:path + '/monthlyCollection/exportExcelDoc.json',
            data:param,
            method:'post'
          });
        });