【问题标题】:JavaScript Blob to download a binary file creating corrupted filesJavaScript Blob 下载二进制文件,创建损坏的文件
【发布时间】:2021-01-21 01:08:03
【问题描述】:

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

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

问题是,服务器上创建的文件通常大小为300 Bytes,而客户端下载的文件大小> 500 Bytes。另外,每当我尝试重用泡菜文件时,它都不会加载,并给出错误:

_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
                }
            }
        });

【问题讨论】:

  • 您的代码(客户端或服务器)可能有问题
  • 所以这是正确的方法,对吧?处理二进制文件有什么特别的吗?
  • this is the correct way - 不确定this 是什么,因为您没有显示任何代码
  • 我已经从我链接的答案中复制了 blob 代码。如果需要任何其他代码 sn-ps,我将附上它们。请告诉我。
  • 在客户端,您使用的是XMLHttpRequestjQuery.ajax 还是其他一些包装器?还可以尝试在服务器端发送 mime 类型:send_file(path, 'application/octet-stream')

标签: javascript python flask blob pickle


【解决方案1】:

奇怪的是,我正在研究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.
...

【讨论】:

  • 看起来您的客户端代码基于我的答案中使用 jQuery.ajax 的旧解决方案。它的问题就是您所看到的,即 jQuery 处理二进制数据。那时,xhrFields 选项不存在。更好的选择是直接使用 XMLHTTPRequest 并设置 xhr.responseType = 'arraybuffer',这就像您使用 blob 一样,可以避免破坏二进制响应。
  • @JonathanAmend 我更改了代码,并替换为xhr.responseType,现在效果很好!
猜你喜欢
  • 1970-01-01
  • 2020-03-15
  • 2019-01-21
  • 1970-01-01
  • 2013-03-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多