假设您的 Java 代码正在使用 Apache HttpComponents(那时您确实应该说的话),那么您的代码在添加时使用
URI aWebImageUrl2 = new URI("http://localhost:1337/");
File imgPath = new File("…/face.png");
final String aImgCaption = "face";
// …
HttpClient httpClient = new DefaultHttpClient();
httpClient.execute(post);
提交以下示例 HTTP 请求(使用 nc -lp 1337 进行测试,请参阅 GNU Netcat):
POST / HTTP/1.1
Content-Length: 990
Content-Type: multipart/form-data; boundary=oQ-4zTK_UL007ymPgBL2VYESjvFwy4cN8C-F
Host: localhost:1337
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.2 (java 1.5)
--oQ-4zTK_UL007ymPgBL2VYESjvFwy4cN8C-F
Content-Disposition: form-data; name="picture"; filename="face.png"
Content-Type: application/octet-stream
�PNG[…]
在 HTML 中执行此类操作的最简单解决方案当然是使用 FORM element 并且不使用或使用最少的客户端脚本:
<form action="http://service.example/" method="POST"
enctype="multipart/form-data">
<input type="file" name="picture">
<input type="submit">
</form>
提交(使用提交按钮或表单对象的submit() 方法提交时)以下示例请求:
POST / HTTP/1.1
Host: localhost:1337
Connection: keep-alive
Content-Length: 886
Cache-Control: max-age=0
Origin: http://localhost
User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.202 Safari/535.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryhC26St5JdG0WUaCi
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Referer: http://localhost/scripts/test/XMLHTTP/file.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: de-CH,de;q=0.8,en-US;q=0.6,en;q=0.4
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
------WebKitFormBoundaryhC26St5JdG0WUaCi
Content-Disposition: form-data; name="picture"; filename="face.png"
Content-Type: image/png
�PNG[…]
但是由于您明确询问了“javascript”解决方案(确实有no such programming language),我推测您希望对提交过程有更多的客户端控制。在这种情况下,您可以使用最新浏览器(不是编程语言)提供的W3C File API 和XMLHttpRequest 或XMLHttpRequest2 API:
<script type="text/javascript">
function isHostMethod(obj, property)
{
if (!obj)
{
return false;
}
var t = typeof obj[property];
return (/\bunknown\b/i.test(t) || /\b(object|function)\b/i.test(t) && obj[property]);
}
var global = this;
function handleSubmit(f)
{
if (isHostMethod(global, "XMLHttpRequest"))
{
try
{
var input = f.elements["myfile"];
var file = input.files[0];
var x = new XMLHttpRequest();
x.open("POST", f.action, false); // ¹
try
{
var formData = new FormData();
formData.append("picture", file);
x.send(formData);
return false;
}
catch (eFormData)
{
try
{
var reader = new FileReader();
reader.onload = function (evt) {
var boundary = "o" + Math.random();
x.setRequestHeader(
"Content-Type", "multipart/form-data; boundary=" + boundary);
x.send(
"--" + boundary + "\r\n"
+ 'Content-Disposition: form-data; name="picture"; filename="' + file.name + '"\r\n'
+ 'Content-Type: application/octet-stream\r\n\r\n'
+ evt.target.result
+ '\r\n--' + boundary + '--\r\n');
};
reader.readAsBinaryString(file);
return false;
}
catch (eFileReader)
{
}
}
}
catch (eFileOrXHR)
{
}
}
return true;
}
</script>
<form action="http://service.example/" method="POST"
enctype="multipart/form-data"
onsubmit="return handleSubmit(this)">
<input type="file" name="myfile">
<input type="submit">
</form>
此方法尝试使用 XMLHttpRequest API。如果失败,函数返回true,所以true被返回给事件处理程序(见属性值),并以通常的方式提交表单(后者可能不适用于您的Web服务;使用前测试通过禁用脚本支持)。
如果可以使用 XMLHttpRequest,则“已测试”² 文件输入是否具有 files 属性并且该文件所引用的对象具有 0 属性(指的是该表单中第一个选择的 File控制(如果支持)。
如果是,则尝试使用 XMLHttpRequest2 API,send() 方法 can take a reference to a FormData 并自行完成所有多部分魔术。如果不支持 XMLHttpRequest2 API(应该抛出异常),则尝试使用 File API 的FileReader,它可以将File 的内容读取为二进制字符串(readAsBinaryString());如果成功(onload),则准备并提交请求。如果其中一种方法看似有效,则不会提交表单 (return false)。
使用 FormData API 使用此代码提交的示例请求:
POST / HTTP/1.1
Host: localhost:1337
Connection: keep-alive
Content-Length: 887
Origin: http://localhost
User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.202 Safari/535.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryLIXsjWnCpVbD8FVA
Accept: */*
Referer: http://localhost/scripts/test/XMLHTTP/file.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: de-CH,de;q=0.8,en-US;q=0.6,en;q=0.4
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
------WebKitFormBoundaryLIXsjWnCpVbD8FVA
Content-Disposition: form-data; name="picture"; filename="face.png"
Content-Type: image/png
�PNG[…]
使用 FileReader API 时,示例请求看起来略有不同(仅作为概念证明):
POST / HTTP/1.1
Host: localhost:1337
Connection: keep-alive
Content-Length: 1146
Origin: http://localhost
User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.202 Safari/535.1
Content-Type: multipart/form-data; boundary=o0.9578036249149591
Accept: */*
Referer: http://localhost/scripts/test/XMLHTTP/file.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: de-CH,de;q=0.8,en-US;q=0.6,en;q=0.4
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
--o0.9578036249149591
Content-Disposition: form-data; name="picture"; filename="face.png"
Content-Type: application/octet-stream
PNG[…]
请注意,XMLHttpRequest2、FormData 和 File API 仅处于 Working Draft 状态,因此仍在不断变化。此外,如果提交的资源和提交的资源使用相同的协议、域和端口号,则此方法有效;您可能需要处理和work around the Same Origin Policy。根据需要添加功能测试和更多异常处理。
另请注意,使用 FileReader 发出的请求对于相同的文件更大,并且缺少前导字符,如 Frits van Campen 提到的问题所示。这可能是由于(WebKit)错误造成的,您可能想要删除此替代方案;我只想说 readAsBinaryString() 方法在文件 API 工作草案中已弃用,而支持 readAsArrayBuffer(),应该使用 Typed Arrays。
另见"Using files from web applications"。
¹ 使用true 进行异步处理;这避免了 UI 阻塞,但需要您在事件侦听器中进行处理,并且您将始终必须取消表单提交(即使 XHR 不成功)。
² 如果无法访问属性,则会抛出异常。如果您更喜欢真正的测试,请实施(附加)功能测试(而不是),并注意并非所有内容都可以安全地进行功能测试。