代码之家  ›  专栏  ›  技术社区  ›  Hassan Baig Altons

在不支持的浏览器上对blob对象使用FormData的append方法

  •  1
  • Hassan Baig Altons  · 技术社区  · 8 年前

    在为用户上传的图像创建Ajax请求时,我使用 FormData() 以以下方式进行实例:

    var form_data = new FormData();
    form_data.append(img_field, img_to_send, img_name);
    

    请注意 img_to_send 是类型的对象 Blob .

    我的问题是 FormData MDN web docs :

    Android 4.0中的XHR为带有blob的FormData发送空内容。

    这与Android浏览器(4.0版)有关。这意味着我正在尝试的xhr FormData 通过 append 可能会在特定浏览器中失败。

    我的问题是,我可以使用什么样的替代方法来确保所述浏览器能够正确处理xhr(我的一个要求)?有一个说明性的例子就好了。

    如果这里没有其他选择,我如何编写代码,使其只在支持 追加 用一个 blob 对象像这样的?

    if (window.FormData.append) {
    // xhr code comes here
    } 
    

    我对那件事很不确定。

    p、 为了回答这个问题,请坚持使用纯JS。

    2 回复  |  直到 6 年前
        1
  •  1
  •   Kaiido    8 年前

    ... 这不是一项容易的任务。。。

    我想问自己的第一个问题是,我是否真的必须支持这个7年的浏览器,而且使用率为0%?

    特征检测:

    我们现在可以通过其 get() , has() , entries() , values() 等方法。但是第一个规范和实现没有这些方法,因此没有提供任何方法来检测这个特定流。

    我没有这样的Android 4浏览器可以检查,但我想他们也没有这些方法。。。

    另一种现代的有点黑客的检查方法是使用ServiceWorker,它应该能够拦截一个虚拟请求,让你知道你的Blob是否被很好地附加,但7年前ServiceWorkers还是不存在的。

    这给我们留下了一个难看的浏览器标识,而不是特征检测(例如 navigator.userAgent 解析)。我不能建议这样做,因为它很容易出错,所以最好让用户知道它在服务器响应中失败了。

    解决方法

    能够 send a generated Blob as binary through an other mean than FormData 几个月前才出现,目前只有Blink浏览器在本机上支持它,FF通过漏洞攻击。

    这意味着您的用户在Android Browser 4上的唯一解决方法。xxx和所有不支持此方法的浏览器,将在其设备上保存生成的Blob,然后从 <input> 并通过普通HTML发送 <form> ,但这是假设他们甚至可以在设备上保存这个Blob,我不能确定。。。

    或者,您可以发送此数据的base64表示形式大30%。

    或者,最好的办法可能是让他们知道应该更新浏览器,因为现在面对网络使用这么旧的浏览器真的很危险。


    下面简要回顾一下不那么糟糕的可能性:

    1. 检查FormData是否可用。否则,在 <表单(>);
    2. 第一次将最佳Blob作为多部分发送。
    3. 在服务器上:检查收到的Blob是否为空。在这种情况下,通过自定义响应让前端知道。
    4. 若服务器说它失败了,请再次发送,这次是b64。因为第一次是空的,所以无论如何,它不应该是一个太重的请求。
        2
  •  1
  •   toto    8 年前

    您好,这是一个如何将文件作为二进制字符串发送到服务器的小示例。 这样就不需要formData了。你可以用简单的邮件发送。 请更改url上载文件。php到您的URL。并阅读服务器应该接收的关于变量示例的注释。

         <!DOCTYPE html>
    
    <html>
        <head>
            <title>TODO supply a title</title>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
    
        </head>
        <body>
            <div>
                <input id="btnFile" type="file" accept="image/*" />
            </div>
            <div style="margin-top: 20px; width: 100px; border: solid 1px red">
                <div id="divProgress" style="background-color: red;width: 10px; height: 5px; margin: 1px"></div>
            </div>
            <div style="margin-top: 20px">
                <input id="btnSend" type="button" value="Send File" />
                <input id= "user_id" type="hidden" value="123"/>
            </div>
    
            <script>
    
    
    
                var btnFile = document.getElementById("btnFile");
                var btnSend = document.getElementById("btnSend");
                var divProgress = document.getElementById("divProgress");
    
    
    
                var selectedFile = null;
    
                //Register event on file selected or changed.
                addEvents(btnFile, "change", function (event) {
                    if (event.target.files.length !== 0) {
                        var file = event.target.files[0];
                        //Check if the file is IMAGE.
                        if (file.type.match("image.*")) {
                            selectedFile = file;
                        } else {
                            selectedFile = null;
                            alert("Please select a IMAGE FILE");
                        }
                    } else {
                        selectedFile = null;
                    }
    
                });
    
                //EVENT BTN SEND.
                addEvents(btnSend, "click", function () {
                    if (selectedFile === null) {
                        //Please select a file to upload.
                        alert("Please select the file.");
                        return;
                    }
    
                    //File reader object.
                    var fl = new FileReader();
    
                    //Add event to read the content file.
                    addEvents(fl, "load", function (evt) {
                        //alert(evt.target.result);
                        try {
    
    
                            //CONVERT ARRAY BUFFER TO BASE64 STRING.
                            var binaryString = evt.target.result;
    
                            //NOW YOU CAN SEND SIMPLE POST DATA.
                            var xhr = new XMLHttpRequest();
    
                            if (supportProgress(xhr)) {
                                addEvents(xhr, "progress", onXHRProgress);
                                addEvents(xhr, "loadstart", onXHRLoadStart);
                                addEvents(xhr, "abort", onXHRAbort);
                            }
    
    
                            xhr.open("POST", "/uploadFile.php", true);
                            //xhr.setRequestHeader("Content-Type", "application/json");
    
                            var user_id = document.getElementById('user_id').value;
    
                            var myData = {
                                uid: user_id,
                                fileName: selectedFile.name,
                                mimeType: selectedFile.type,
                                extension: getFileExtension(selectedFile),
                                contentFile: binaryString
    
                            };
                            xhr.send(JSON.stringify(myData));
    
                            /*
                             * IN YOUR SERVER SIDE YOU GET THE POST VARIABLE.
                             * fileName = The name of the file.
                             * mimeType = example "image/png"
                             * extension = png
                             * conentFile = Binary String of the content file and you can convert the Binary String to File in your disk according extension or mimeType
                             */
    
                        } catch (e) {
    
                        }
    
                    });
    
                    //Read the file as arraybuffer.
                    fl.readAsBinaryString(selectedFile);
                });
    
    
                function onXHRProgress(e) {
                    var loaded = 0;
                    if (e.lengthComputable) {
                        if (e.loaded === e.total) {
                            loaded = 100;
                            selectedFile = null;
                        } else {
                            loaded = Math.round((e.loaded * 100) / e.total);
                        }
    
                        //Change the progress here.
                        divProgress.style.width = loaded + "px";
                    }
                }
    
                function onXHRLoadStart() {
                    divProgress.style.width = "0px";
                }
    
                function onXHRAbort() {
                    selectedFile = null;
    
                }
    
    
    
                function getFileExtension(file) {
                    var fileName = file.name;
                    var i = fileName.toString().lastIndexOf(".");
                    if (i !== -1) {
                        return fileName.toString().substring((i + 1), fileName.toString().length).toLowerCase();
                    } else {
                        return "";
                    }
    
                }
    
    
                function supportProgress(xhr) {
                    return !!(xhr && ('upload' in xhr) && ('onprogress' in xhr.upload));
                }
    
                function addEvents(obj, evtName, func) {
                    if (obj.addEventListener !== undefined && obj.addEventListener !== null) {
                        obj.addEventListener(evtName, func, false);
                    } else if (obj.attachEvent !== undefined && obj.attachEvent !== null) {
                        obj.attachEvent(evtName, func);
                    } else {
                        if (this.getAttribute("on" + evtName) !== undefined) {
                            obj["on" + evtName] = func;
                        } else {
                            obj[evtName] = func;
                        }
                    }
    
                }
    
                function removeEvents(obj, evtName, func) {
                    if (obj.removeEventListener !== undefined && obj.removeEventListener !== null) {
                        obj.removeEventListener(evtName, func, false);
                    } else if (obj.detachEvent !== undefined && obj.detachEvent !== null) {
                        obj.detachEvent(evtName, func);
                    } else {
                        if (this.getAttribute("on" + evtName) !== undefined) {
                            obj["on" + evtName] = null;
                        } else {
                            obj[evtName] = null;
                        }
                    }
    
                }
    
    
    
    
    
    
            </script>
        </body>
    </html>