如何以 POST 请求的形式上传单独选择的文件? [复制]

Posted

技术标签:

【中文标题】如何以 POST 请求的形式上传单独选择的文件? [复制]【英文标题】:how to upload separately selected files in form POST request? [duplicate] 【发布时间】:2018-07-14 20:08:59 【问题描述】:

我想要什么

人们点击添加图片按钮,他们选择一张图片,图片被添加到图库中。

他们可以通过单击叉号删除图像,然后重新单击添加图像按钮以添加更多图像。

这一切都有效,我引用了所有 File 元素。

但是,我不知道如何在表单的 post 请求中发送文件。

问题问题是你不能从文件数组中创建FileList,或者将文件数组设置为input.files = arrOfFiles

输入元素本身不允许您添加更多文件或删除文件...它只是用新文件替换旧文件。

这不是我想要的,因此我在 js 中保持对文件对象的引用,并让用户删除图像或添加更多图像。

我知道我可以将单个文件作为 XHR 发送,但我想通过已经存在的表单发送它们。

我想知道一种通过表单而不是js发送文件的方法,但显然这是不可能的

【问题讨论】:

不可能。只需转换为 base 64 并上传到服务器 @MikeMcCaughan 不,那是询问如何通过 ajax 发送数据。我在问如何通过表单发送文件但能够修改它 不清楚您在做什么或为什么...如果您想通过表单发送它们,请使用input type="file" 元素通过表单发送它们。您可以使用 DOM 操作 input 元素的列表。无需将数据加载到 javascript 中。 输入元素允许您选择多个文件,因此如果用户想要删除选择上传的文件中的1个,则没有办法。是的,我只能让用户每次上传选择 1 个文件,并在用户将其从图库中删除时删除该输入元素 【参考方案1】:

这正是 FormData API 的用途:从头开始创建表单数据,您可以将其上传到服务器,就好像它是从 <form> 对象创建的一样,除了您可以控制那里的内容或不是。

所以要在 FormData 中附加一个文件或一个 Blob,代码是

var fd = new FormData();
fd.append(field_name, blob, file_name);

要追加多个文件,您可以再次调用fd.append,但请注意,后端通常需要将field_name 格式化为他们可以知道此处需要多个值的方式。 通常这是通过在您的字段名后添加[] 来完成的。

var fd = new FormData();
fd.append('files[]', blob_1, file_name_1);
fd.append('files[]', blob_2, file_name_2);

然后您可以通过 AJAX 请求将其发送到您的服务器,这不会影响此请求与由单个 <input multiple name="files[]"> 发出的真实请求。

请注意,对于文件,file_name 是可选的,如果未设置,则默认为文件的名称。但是,如果您不想设置随机名称,则 Blob 需要它。

var file_1 = new File(['foo'], 'file1.txt',type:'text/plain');
var file_2 = new File(['bar'], 'file2.txt', type:'text/plain');

var fd = new FormData();
fd.append('files[]', file_1);
fd.append('files[]', file_2);

console.log(...fd.entries());

// and to send it to your server
var xhr = new XMLHttpRequest();
xhr.open('POST', 'your_server_url');
xhr.send(fd);

【讨论】:

所以我想我会拦截表单提交,获取表单数据并将文件添加到表单数据然后发送它,并在发送时重定向到服务器会告诉的任何地方 @MuhammadUmer 就是这样。在表单的submit 事件中,调用event.preventDefault(),然后构建您的FormData。请注意,如果您的 <form> 中有其他字段,您甚至可以通过调用 new FormData(your_form_element) 让浏览器从当前状态创建 FormData。 @MuhammadUmer 所以再一次快速的谷歌会告诉你输入多个是只读的,如果用户再次选择,则会覆盖。所以kaiido只是告诉你我们大多数人告诉你的。这不可能。你必须自己动手。如何实施取决于您。 @Darkrum 我不确定你来自哪里,但我并不是说不可能,恰恰相反。此外,您似乎弄错了问题:OP 不想拥有一个 <input multipe>,他希望获得多个 <input>s,以便他可以使用自己的 UI 处理这些文件。最后,是的you can change the FileList of an input,即使它仍然是一个黑客,FormData 仍然是处理这种情况的最佳方式。 @Darkrum 是的,我明白了。但是,缺少功能。如果它只是安全漏洞,那么你怎么能通过 Ajax 做到这一点,没有意义。但是是的,我明白了,他说的是同样的事情,使用 js 发送文件,并且不能通过表单和 1 个输入元素来完成。但是,我可以使用多个输入并让用户为每个输入选择一个文件,但这会很糟糕【参考方案2】:

如果您只是要上传图片。考虑使用 base64 编码。这就是我所做的。分配 base64 编码的 URL 以对 JSON 进行字符串化,或者您希望对您更容易的任何内容。

当数据被 POST 后,在那里进行转换,以取回您的文件以保存它们。解码 base64 将取决于您在问题中未提及的后端语言。

在 JavaScript 中,您可以使用此函数来加载您提到的处于预览状态的图像。

 function previewImage (inputId, eleId) 
        if (window.FileReader) 
                var oPreviewImg = null, oFReader = new window.FileReader(),
                        rFilter = /^(?:image\/bmp|image\/cis\-cod|image\/gif|image\/ief|image\/jpeg|image\/jpeg|image\/jpeg|image\/pipeg|image\/png|image\/svg\+xml|image\/tiff|image\/x\-cmu\-raster|image\/x\-cmx|image\/x\-icon|image\/x\-portable\-anymap|image\/x\-portable\-bitmap|image\/x\-portable\-graymap|image\/x\-portable\-pixmap|image\/x\-rgb|image\/x\-xbitmap|image\/x\-xpixmap|image\/x\-xwindowdump)$/i;

                oFReader.onload = function (oFREvent) 
                    if (!oPreviewImg) 
                        var newPreview = document.getElementById(eleId);
                        oPreviewImg = new Image();
                        oPreviewImg.style.width = (newPreview.offsetWidth).toString() + "px";
                        oPreviewImg.style.height = (newPreview.offsetHeight).toString() + "px";
                        if(newPreview.children.length > 0)
                            newPreview.replaceChild(oPreviewImg, newPreview.children[0]);
                        else
                            newPreview.appendChild(oPreviewImg);
                    
                    oPreviewImg.src = oFREvent.target.result;
                    // Add Bootstrap's img-thumbnail class to the image frame
                    oPreviewImg.classList.add("img-thumbnail");
                ;

                return function () 
                    var aFiles = document.getElementById(inputId).files;
                    if (aFiles.length === 0)  return; 
                    if (!rFilter.test(aFiles[0].type))  alert("You must select a valid image file!"); return; 
                    oFReader.readAsDataURL(aFiles[0]);
                


        
        if (navigator.appName === "Microsoft Internet Explorer") 
            return function () 
                document.getElementById(eleId).filters.item("DXImageTransform.Microsoft.AlphaImageLoader").src = document.getElementById(inputId).value;
            
        


    ;

然后你可以从img元素的src属性中提取base64 URL。

您可能会想出自己喜欢的“取消”上传的方法,可能会破坏 img 元素。

【讨论】:

为什么要改回来?那是资源的浪费。客户可以轻松为您完成。 上传到S3? @Darkrum 如果他想在服务器端用不同的语言用它做任何其他事情。 @MuhammadUmer 从未使用过 S3,因此不确定他们如何处理它。你可以试试。 我的意思是你不会以文本形式将图像上传到s3

以上是关于如何以 POST 请求的形式上传单独选择的文件? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

PHP如何通过CURL上传文件

从 Electron 应用程序发出多部分 POST 请求以进行文件上传

aiohttp 异步http请求-4.文件上传multipart/form-data

如何以多部分形式将画布图像上传到 java servlet?

Postman Post请求上传文件

如何存储来自 Post-Response 的 JWT 并以 HTML 形式发送?