JavaScript Blob 用于下载创建损坏文件的二进制文件

JavaScript Blob to download a binary file creating corrupted files

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

接下来需要自动下载这个文件到客户端,所以我用了this answer.

问题是,在服务器上创建的文件通常大小为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]*=((['"]).*?|[^;\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
                }
            }
        });

奇怪的是,我正在研究 this 答案,结果很有效。所以,我补充说:

xhrFields: {
    responseType:'blob'
},

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

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


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.
...