未定义 FormData 时如何在 Web Workers 中上传文件

Posted

技术标签:

【中文标题】未定义 FormData 时如何在 Web Workers 中上传文件【英文标题】:How to upload files in Web Workers without using FormData 【发布时间】:2012-12-01 23:49:18 【问题描述】:

当我通过 Web Worker 上传时,如何在 php 中检索$_FILES?当我尝试使用FormData 时,出现以下错误:

错误:FormData 未定义

这是我的代码:

function uploadFile(blobFile, fileName, filePart, totalChunks) 
    //if I try to put this
    //var formData = new FormData(); //it does not work
    var xhr = new XMLHttpRequest();                
    xhr.open("POST", "upload.php"+"?"+"file="+fileName + filePart, true);
    xhr.onload = function(e) ;
    xhr.send(blobFile);
   

那么在 upload.php 中我应该如何从 $_FILES 获取 tmp 路径?以防万一,我还将显示引用 Web Worker 的页面:

<form id="fileuploader" enctype="multipart/form-data" method="post" action="upload.php">
    <label for="fileToUpload">Select Files to Upload</label><br />
    <input type="file" name="fileToUpload[]" multiple="" id="fileToUpload" onchange="fileList();"/><br />
    <input type="button" onclick="sendRequest();" value="Upload" />
    <!-- a place for File Listing -->
    <div id="fileList"></div>           
</form>
<script type="text/javascript">
function sendRequest() 
    var worker = new Worker("fileupload.js");
    worker.onmessage = function(e) 
        alert(e.data);
    
    var file = document.getElementById('fileToUpload');
    for(var i = 0; i < file.files.length; i++)  
        worker.postMessage(file.files[i]);  
    

【问题讨论】:

formData 对象需要访问 DOM 才能获取表单,而 webworkers 没有访问 DOM 的权限,所以这两者不能一起工作。这并不是 webworkers 的真正用途。 @adeneo 所以你的意思是,没有办法让这个工作?不使用formdata还有其他方法吗?因为根据这篇文章使用webworker上传文件kongaraju.blogspot.com/2012/07/…看起来不错,但他没有显示服务器部分 不确定,我只知道网络工作者永远无法访问 DOM,而 FormData 几乎基于表单构建对象,如果没有访问 DOM,很难让 FormData 工作,因为它根本无法访问表单。 @adeneo OP 不会尝试使用 &lt;form&gt; 对象(这是不可能的)。相反,他发布了File 对象,由于structured clone algorithm,这些对象被正确接收。正如我在下面演示的那样,没有 DOM 并不意味着不能使用 FormData API - 请参阅下面的答案。 【参考方案1】:

我编写了以下 polyfill 来模拟 Web Workers 中的 FormData 方法。由于 Web Worker 不支持 DOM,因此也不支持 new FormData(&lt;htmlFormElement&gt;); 构造函数调用。尽管 polyfill 支持 FileBlob 对象、类型化数组和字符串。

它最初是作为Upload a File in a Google Chrome Extension 答案的一部分发布的。要查看如何使用它的示例,请查看其他答案。

/*
 * FormData for XMLHttpRequest 2  -  Polyfill for Web Worker  (c) 2012 Rob W
 * License: Creative Commons BY - http://creativecommons.org/licenses/by/3.0/
 * - append(name, value[, filename])
 * - toString: Returns an ArrayBuffer object
 * 
 * Specification: http://www.w3.org/TR/XMLHttpRequest/#formdata
 *                http://www.w3.org/TR/XMLHttpRequest/#the-send-method
 * The .append() implementation also accepts Uint8Array and ArrayBuffer objects
 * Web Workers do not natively support FormData:
 *                http://dev.w3.org/html5/workers/#apis-available-to-workers
 **/
(function() 
    // Export variable to the global scope
    (this == undefined ? self : this)['FormData'] = FormData;

    var ___send$rw = XMLHttpRequest.prototype.send;
    XMLHttpRequest.prototype['send'] = function(data) 
        if (data instanceof FormData) 
            if (!data.__endedMultipart) data.__append('--' + data.boundary + '--\r\n');
            data.__endedMultipart = true;
            this.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + data.boundary);
            data = new Uint8Array(data.data).buffer;
        
        // Invoke original XHR.send
        return ___send$rw.call(this, data);
    ;

    function FormData() 
        // Force a Constructor
        if (!(this instanceof FormData)) return new FormData();
        // Generate a random boundary - This must be unique with respect to the form's contents.
        this.boundary = '------RWWorkerFormDataBoundary' + Math.random().toString(36);
        var internal_data = this.data = [];
        /**
        * Internal method.
        * @param inp String | ArrayBuffer | Uint8Array  Input
        */
        this.__append = function(inp) 
            var i=0, len;
            if (typeof inp === 'string') 
                for (len=inp.length; i<len; i++)
                    internal_data.push(inp.charCodeAt(i) & 0xff);
             else if (inp && inp.byteLength) /*If ArrayBuffer or typed array */
                if (!('byteOffset' in inp))   /* If ArrayBuffer, wrap in view */
                    inp = new Uint8Array(inp);
                for (len=inp.byteLength; i<len; i++)
                    internal_data.push(inp[i] & 0xff);
            
        ;
    
    /**
    * @param name     String                                  Key name
    * @param value    String|Blob|File|Uint8Array|ArrayBuffer Value
    * @param filename String                                  Optional File name (when value is not a string).
    **/
    FormData.prototype['append'] = function(name, value, filename) 
        if (this.__endedMultipart) 
            // Truncate the closing boundary
            this.data.length -= this.boundary.length + 6;
            this.__endedMultipart = false;
        
        var valueType = Object.prototype.toString.call(value),
            part = '--' + this.boundary + '\r\n' + 
                'Content-Disposition: form-data; name="' + name + '"';

        if (/^\[object (?:Blob|File)(?:Constructor)?\]$/.test(valueType)) 
            return this.append(name,
                            new Uint8Array(new FileReaderSync().readAsArrayBuffer(value)),
                            filename || value.name);
         else if (/^\[object (?:Uint8Array|ArrayBuffer)(?:Constructor)?\]$/.test(valueType)) 
            part += '; filename="'+ (filename || 'blob').replace(/"/g,'%22') +'"\r\n';
            part += 'Content-Type: application/octet-stream\r\n\r\n';
            this.__append(part);
            this.__append(value);
            part = '\r\n';
         else 
            part += '\r\n\r\n' + value + '\r\n';
        
        this.__append(part);
    ;
)();

【讨论】:

完美!但不要忘记更改内容类型!

以上是关于未定义 FormData 时如何在 Web Workers 中上传文件的主要内容,如果未能解决你的问题,请参考以下文章

如何将带有附加数据的 FormData 文件发送到 asp.net web api ajax 调用

在 iOS 上未选择文件时,JavaScript 使用 FormData 和 jQuery 的 ajax 上传文件返回错误 500

未通过 AJAX POST 请求发送大尺寸图像的 base64(作为 FormData 参数)

错误类型“Microsoft.Office.Interop.Word.ApplicationClass”未定义构造函数

如何使用 fetch 将 FormData 从 javascript 发送到 ASP.NET Core 2.1 Web API

Flutter 多文件上传( FormData )