JavaScript Blob下载一个二进制文件,创建损坏的文件 [英] JavaScript Blob to download a binary file creating corrupted files

查看:61
本文介绍了JavaScript Blob下载一个二进制文件,创建损坏的文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个二进制文件(准确地说是python pickle文件).每当请求这样的文件时,我都会在服务器端创建一个文件,然后将其作为AJAX请求通过flask的send_file发送到客户端.

接下来,我需要将此文件自动下载到客户端,因此我使用了此答案.. >

问题在于,在服务器上创建的文件通常具有300字节的大小,而在客户端下载的文件的大小则大于500字节.另外,每当我尝试重用pickle文件时,该文件都不会加载,并显示错误消息:

_pickle.UnpicklingError: invalid load key, '\xef'.

因此,服务器文件是无缝加载的.因此,问题在于,客户端文件在传输时已损坏.我认为js blob可能是罪魁祸首.

以前有没有人看过类似的东西?


处理AJAX的服务器端代码(烧瓶)

@app.route("/_exportTest",methods=['POST'])
def exportTest():
    index = int(request.form['index'])
    path = g.controller.exportTest(testID=index)
    logger.debug("Test file path :"+path)
    return send_file(path) #this is wrong somehow

关于exportTest功能:

def exportTest(self,testName):
    dic = dict() 
    dic['screenShot'] = self.screenShot #string
    dic['original_activity'] = self.original_activity #string
    dic['steps'] = self.steps #list of tuples of strings
    if self.exportFilePath=='.': #this is the case which will be true
        filePath = os.path.join(os.getcwd(),testName) 
    else:
        filePath = os.path.join(os.getcwd(),self.exportFilePath,testName)
    logger.debug("filePath :"+filePath)
    try:
        pickle.dump(dic,open(filePath,"wb"),protocol=pickle.HIGHEST_PROTOCOL)
    except Exception as e:
        logger.debug("Error while pickling Test.\n Error :"+str(e)) #No such error was printed
    return filePath


客户端代码:

$.ajax({

            type: "POST",
            // url: "/_exportTest",
            url:"/_exportTest",
            data:{index:testIndex},
            success: function(response, status, xhr) {
                // check for a filename
                var filename = "TEST_"+testIndex+".tst";
                var disposition = xhr.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, '');
                }
                
                var type = xhr.getResponseHeader('Content-Type');
                var blob = new Blob([response],{type:type});//, { type: type });
                console.log("Binary type :"+type) ;
                if (typeof window.navigator.msSaveBlob !== 'undefined') {
                    // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
                    console.log("WINDOW NAVIGATION MSSAVEBLOB type if undefined") ;
                    window.navigator.msSaveBlob(blob, filename);
                } 
                else {
                    console.log("ELSE1")
                    var URL = window.URL || window.webkitURL;
                    var downloadUrl = URL.createObjectURL(blob);

                    if (filename) {
                        console.log("Filename exists") ;
                        // use HTML5 a[download] attribute to specify filename
                        var a = document.createElement("a");
                        // safari doesn't support this yet
                        if (typeof a.download === 'undefined') {
                            console.log("typeof a.download is undefined") ;
                            window.location.href = downloadUrl;
                        } else {
                            console.log("typeof a.download is not undefined") ;
                            a.href = downloadUrl;
                            a.download = filename;
                            document.body.appendChild(a);
                            a.click();
                        }
                    } else {
                        console.log("Filename does not exist") ;
                        window.location.href = downloadUrl;
                    }
                    // window.location.href = downloadUrl;
                    setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup
                }
            }
        });

解决方案

奇怪的是,我正在研究答案,该方法有效.因此,我添加了:

xhrFields: {
    responseType:'blob'
},

在AJAX请求中,它为我解决了问题.

我完全不知道,为什么这样做有效,所以有人能给出比这个更好的答案吗?


MDN文档中:

The values supported by responseType are the following:
An empty responseType string is treated the same as "text", the default type.
arraybuffer
The response is a JavaScript ArrayBuffer containing binary data.
blob
The response is a Blob object containing the binary data.
...

I have a binary file (python pickle file, to be exact). Whenever such a file is requested, I create one on server side, and then send it to the client via flask's send_file as an AJAX request.

Next, I need to download this file automatically to the client side, so I have used this answer.

The problem is that, the created file on the server normally has a size 300 Bytes, and the file downloaded on the client side is of the size >500 Bytes. Plus whenever I try to reuse the pickle file, it doesn't load, giving the error:

_pickle.UnpicklingError: invalid load key, '\xef'.

Whereas, the server file is loaded seamlessly. So, the problem is, the client side file is corrupted, while in transmission. I think the js blob might be the culprit.

Has anyone seen something like this before?


Server side code handling the AJAX (flask)

@app.route("/_exportTest",methods=['POST'])
def exportTest():
    index = int(request.form['index'])
    path = g.controller.exportTest(testID=index)
    logger.debug("Test file path :"+path)
    return send_file(path) #this is wrong somehow

Regarding the exportTest function:

def exportTest(self,testName):
    dic = dict() 
    dic['screenShot'] = self.screenShot #string
    dic['original_activity'] = self.original_activity #string
    dic['steps'] = self.steps #list of tuples of strings
    if self.exportFilePath=='.': #this is the case which will be true
        filePath = os.path.join(os.getcwd(),testName) 
    else:
        filePath = os.path.join(os.getcwd(),self.exportFilePath,testName)
    logger.debug("filePath :"+filePath)
    try:
        pickle.dump(dic,open(filePath,"wb"),protocol=pickle.HIGHEST_PROTOCOL)
    except Exception as e:
        logger.debug("Error while pickling Test.\n Error :"+str(e)) #No such error was printed
    return filePath


Client side code:

$.ajax({

            type: "POST",
            // url: "/_exportTest",
            url:"/_exportTest",
            data:{index:testIndex},
            success: function(response, status, xhr) {
                // check for a filename
                var filename = "TEST_"+testIndex+".tst";
                var disposition = xhr.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, '');
                }
                
                var type = xhr.getResponseHeader('Content-Type');
                var blob = new Blob([response],{type:type});//, { type: type });
                console.log("Binary type :"+type) ;
                if (typeof window.navigator.msSaveBlob !== 'undefined') {
                    // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
                    console.log("WINDOW NAVIGATION MSSAVEBLOB type if undefined") ;
                    window.navigator.msSaveBlob(blob, filename);
                } 
                else {
                    console.log("ELSE1")
                    var URL = window.URL || window.webkitURL;
                    var downloadUrl = URL.createObjectURL(blob);

                    if (filename) {
                        console.log("Filename exists") ;
                        // use HTML5 a[download] attribute to specify filename
                        var a = document.createElement("a");
                        // safari doesn't support this yet
                        if (typeof a.download === 'undefined') {
                            console.log("typeof a.download is undefined") ;
                            window.location.href = downloadUrl;
                        } else {
                            console.log("typeof a.download is not undefined") ;
                            a.href = downloadUrl;
                            a.download = filename;
                            document.body.appendChild(a);
                            a.click();
                        }
                    } else {
                        console.log("Filename does not exist") ;
                        window.location.href = downloadUrl;
                    }
                    // window.location.href = downloadUrl;
                    setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup
                }
            }
        });

解决方案

Weirdly enough, I was looking into this answer, which worked. So, I added :

xhrFields: {
    responseType:'blob'
},

in the AJAX request, which solved the problem for me.

I have absolutely no idea, why this worked, so can someone give a better answer than this?


At MDN Docs:

The values supported by responseType are the following:
An empty responseType string is treated the same as "text", the default type.
arraybuffer
The response is a JavaScript ArrayBuffer containing binary data.
blob
The response is a Blob object containing the binary data.
...

这篇关于JavaScript Blob下载一个二进制文件,创建损坏的文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆