Javascript:上传文件...没有文件

IT技术 javascript file-upload extjs
2021-02-06 01:32:02

我试图在不实际使用用户输入的文件的情况下伪造文件上传。文件的内容将从字符串动态生成。

这可能吗?以前有人这样做过吗?是否有可用的示例/理论?

澄清一下,我知道如何使用隐藏的 iframe 和朋友使用 AJAX 技术上传文件 - 问题是上传不在表单中的文件。

我正在使用 ExtJS,但 jQuery 也是可行的,因为 ExtJS 可以插入它(ext-jquery-base)。

6个回答

如果您不需要对旧浏览器的支持,您可以使用 FormData 对象,它是 File API 的一部分:

var formData = new FormData();
var blob = new Blob(['Lorem ipsum'], { type: 'plain/text' });
formData.append('file', blob,'readme.txt');

var request = new XMLHttpRequest();
request.open('POST', 'http://example.org/upload');
request.send(formData);

当前所有浏览器都支持 File Api (IE10+)

这应该是公认的答案 - 我花了 8 个小时梳理了各种帖子,这就是有效的方法,而且代码很少。
2021-03-14 01:32:02
我避免编写自己的 XMLHttpRequests。这绝对是我的首选答案!
2021-04-06 01:32:02

为什么不直接XMLHttpRequest()与 POST 一起使用

function beginQuoteFileUnquoteUpload(data)
{
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "http://www.mysite.com/myuploadhandler.php", true);
    xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    xhr.onreadystatechange = function ()
    {
        if (xhr.readyState == 4 && xhr.status == 200)
            alert("File uploaded!");
    }
    xhr.send("filedata="+encodeURIComponent(data));
}

服务器上的处理程序脚本只是将文件数据写入文件。

编辑
文件上传仍然是具有不同内容类型的 http 帖子。您可以使用此内容类型并用边界分隔您的内容:

function beginQuoteFileUnquoteUpload(data)
{
    // Define a boundary, I stole this from IE but you can use any string AFAIK
    var boundary = "---------------------------7da24f2e50046";
    var xhr = new XMLHttpRequest();
    var body = '--' + boundary + '\r\n'
             // Parameter name is "file" and local filename is "temp.txt"
             + 'Content-Disposition: form-data; name="file";'
             + 'filename="temp.txt"\r\n'
             // Add the file's mime-type
             + 'Content-type: plain/text\r\n\r\n'
             + data + '\r\n'
             + boundary + '--';

    xhr.open("POST", "http://www.mysite.com/myuploadhandler.php", true);
    xhr.setRequestHeader(
        "Content-type", "multipart/form-data; boundary="+boundary

    );
    xhr.onreadystatechange = function ()
    {
        if (xhr.readyState == 4 && xhr.status == 200)
            alert("File uploaded!");
    }
    xhr.send(body);
}

如果你想发送额外的数据,你只需用边界分隔每个部分,并描述每个部分的 content-disposition 和 content-type 标题。每个标题由一个换行符分隔,正文与标题由一个额外的换行符分隔。当然,以这种方式上传二进制数据会稍微困难一些:-)

进一步编辑:忘记提及,确保任何边界字符串不在您发送的文本“文件”中,否则它将被视为边界。

@Andy:没关系,我必须多次阅读 RFC 才能使其正常工作!
2021-03-17 01:32:02
我想他想知道如何生成data.
2021-03-19 01:32:02
因为服务器不会将其识别为上传的“文件”。
2021-03-25 01:32:02
@LiraNuna:如果您是从字符串生成内容,为什么这很重要?不能把它识别为一个字符串并写出来吗?
2021-03-31 01:32:02
这将需要我更改服务器端代码,这在我的情况下是不可能的(远程服务)。
2021-04-07 01:32:02

只是分享最终结果,它有效 - 并且具有添加/删除参数的简洁方法,无需硬编码任何内容。

var boundary = '-----------------------------' +
            Math.floor(Math.random() * Math.pow(10, 8));

    /* Parameters go here */
var params = {
    file: {
        type: 'text/plain',
        filename: Path.utils.basename(currentTab.id),
        content: GET_CONTENT() /* File content goes here */
    },
    action: 'upload',
    overwrite: 'true',
    destination: '/'
};

var content = [];
for(var i in params) {
    content.push('--' + boundary);

    var mimeHeader = 'Content-Disposition: form-data; name="'+i+'"; ';
    if(params[i].filename)
        mimeHeader += 'filename="'+ params[i].filename +'";';
    content.push(mimeHeader);

    if(params[i].type)
        content.push('Content-Type: ' + params[i].type);

    content.push('');
    content.push(params[i].content || params[i]);
};

    /* Use your favorite toolkit here */
    /* it should still work if you can control headers and POST raw data */
Ext.Ajax.request({
    method: 'POST',
    url: 'www.example.com/upload.php',
    jsonData: content.join('\r\n'),
    headers: {
        'Content-Type': 'multipart/form-data; boundary=' + boundary,
        'Content-Length': content.length
    }
});

这经过测试可以在所有现代浏览器上运行,包括但不限于:

  • IE6+
  • FF 1.5+
  • 歌剧 9+
  • 铬 1.0+
  • Safari 3.0+
+1 不错的解决方案。但我认为你的算法有问题。为什么你使用 afor in作为 params 对象?它看起来像是为多个文件准备的,但是第二个文件将如何在对象中命名?在哪里actionoverwritedestination使用?以及他们如何不破坏里面的代码for in
2021-03-24 01:32:02
@LiraNuna,我看到你们和好对所有魔法-----------------------------,通过唯一的要求的MIME规范(参见RFC 1341,秒7.2.1)是与边界动工--后跟有效的令牌(参见RFC 1341第4节)。希望这也能帮助其他人了解他们的自由:-)
2021-03-25 01:32:02
你好,这段代码不太正确。内容长度计算不正确 - 它不包括数组连接中的 '\r\n'。同样从技术上讲,这并不能正确地进行边界。最初应该是“--边界”,然后是部分之间的“边界”,然后是“边界--”。有了这些修复程序,我对 Tomcat/JBoss 似乎可以正常工作。做得好 :-)
2021-04-09 01:32:02
@Protron:我使用的原因for( in )是从描述对象中获取键。代码将检测是否filename在嵌套对象(描述要上传的文件)上设置。其他参数 ( overwrite, action, destination) 只是像使用表单一样传递的额外参数。
2021-04-13 01:32:02

文件上传只是一个POST文件内容正确编码并带有特殊multipart/formdata标头请求您需要使用它,<input type=file />因为您的浏览器安全性禁止您直接访问用户磁盘。

由于您不需要读取用户磁盘YES,您可以使用 Javascript 伪造它。这将只是一个XMLHttpRequest. 要伪造“真实”上传请求,您可以安装Fiddler并检查您的传出请求。

您需要正确编码该文件,因此此链接非常有用:RFC 2388:从表单返回值:multipart/form-data

那么该请求中应该包含哪些内容?该协议是如何定义的?怎么造假?
2021-03-15 01:32:02
我没有使用 Fiddler(此处为 Linux 用户),但 Firebug 确实显示了它的外观。这让我更近了一步。我正在投票,因为它很有帮助,但尚未选择答案。
2021-04-02 01:32:02
这不是一个协议,它只是一个普通的 HTTP 请求;我更新了我的答案
2021-04-11 01:32:02

使用 jQuery 模仿“假”文件上传的简单方法:

var fd = new FormData();
var file = new Blob(['file contents'], {type: 'plain/text'});

fd.append('formFieldName', file, 'fileName.txt');

$.ajax({
  url: 'http://example.com/yourAddress',
  method: 'post',
  data: fd,
  processData: false,        //this...
  contentType: false         //and this is for formData type
});