JavaScript中的图像上传到Web服务

7

我需要从Javascript上传一张图片到webservice。我需要同时发送一个JSON字符串和一张图片文件。在Java中,我们有MultipartEntity。以下是我的Java代码:

HttpPost post = new HttpPost( aWebImageUrl2 );
MultipartEntity entity  = new MultipartEntity( HttpMultipartMode.BROWSER_COMPATIBLE );
// For File parameters
entity.addPart( "picture", new FileBody((( File ) imgPath )));
// For usual String parameters
entity.addPart( "url", new StringBody( aImgCaption, "text/plain", Charset.forName( "UTF-8" )));
post.setEntity( entity );  

现在我需要在JavaScript中进行相同的图片上传。
但是在JavaScript中,我没有找到MultipartEntity的任何等效物。请建议任何解决方案。


你在服务器端运行什么(PHP、ASP.Net等)? - Greg
@ Greg,该网络服务使用Java编写,是一个Restful网络服务。这些网络服务是由某人编写的。我只需要访问它。我无法更改它。 - vissu
5个回答

5
为了上传图片,我使用Valum的ajax上传插件jQuery表单插件,可以以ajax方式提交普通表单。
如果您使用POST请求,则不要忘记使用MAX_FILE_SIZE隐藏属性: <input type="hidden" name="MAX_FILE_SIZE" value="20000000"> 请注意,它必须在文件输入字段之前。它是以字节为单位的,因此这将限制上传到20MB。有关详细信息,请参见PHP文档

@kommradHomer,我只在Opera浏览器中使用它,对于其他浏览器,我使用Valum的插件——主要优点是不需要表单——只需单击即可添加图像。 - Tomas

2
假设您的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元素,并且不需要或只需要最少的客户端脚本。
<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”解决方案(实际上并没有这样的编程语言),我推断您想在提交过程中拥有更多的客户端控制。在这种情况下,您可以使用最近浏览器提供的W3C文件APIXMLHttpRequestXMLHttpRequest2 API:(注意,这些是浏览器提供的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,该API的send()方法可以接受一个对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 仅处于“工作草案”状态,因此仍在不断变化。此外,如果提交资源的来源和提交目标资源使用的协议、域名和端口号不同,则您可能需要处理并解决同源策略问题。根据需要添加功能测试和更多异常处理。
还要注意,使用 FileReader 发送的请求比相同文件更大,并且缺少前导字符,如 Frits van Campen 所提到的问题所示。这可能是由于 (WebKit) 中的错误,您可能想要删除此替代方案;我只需说一句,readAsBinaryString() 方法已经被 File API 工作草案中的 readAsArrayBuffer() 方法取代,应使用 Typed Arrays
另请参见 "从 Web 应用程序使用文件"

1. 使用true进行异步处理;这样可以避免UI阻塞,但需要在事件监听器中进行处理,并且您始终需要取消表单提交(即使XHR不成功)。

2. 如果无法访问属性,则会抛出异常。如果您更喜欢真实的测试,请实现(额外的)功能测试,并注意并非所有内容都可以安全地进行功能测试。


0

0

实际上,您可以使用JavaScript调用服务,这里有一个示例代码。

如果您的要求是从JS上传图像并进行webservice调用,则可能会有些棘手。

您可以简单地将图像上传到服务器,并让服务器调用webservice。有许多工具可帮助您将文件上传到服务器


Anantha Sharma,我有一个由某人用Java编写的Restful Web服务。我必须使用JavaScript直接从HTML页面上传图像和JSON消息到Web服务(不能使用任何服务器端脚本)。我需要使用JavaScript将上传的图像和JSON消息作为多个部分(MultipartEntity)发送。 - vissu

0

我以前做过这个,使用HTML5的canvas元素。我将在这里使用jQuery。我假设一个通用的图像大小为300px x 300px。

首先,在您的页面上添加一个隐藏的画布:

$("body").append('<canvas id="theCanvas" style="display:none" width="300px" height="300px"></canvas>');

接下来,将图像加载到画布上:

var canvas = document.getElementById('theCanvas');  
var context = canvas.getContext('2d');
var imageObj = new Image();
imageObj.src = "/path/to/image.jpg";  
context.drawImage(imageObj, 0, 0, 300, 300);

现在,您可以将画布上的内容作为数据字符串访问,并使用jQuery的post函数将其发布到Web服务:
$.post("path/to/service", {'image':canvas.toDataURL("image/png"), 'url':'caption'},  function(file){               
    //Callback code
});  

1
这将发送一个Base64编码的PNG文件,该文件在前面有一些附加标头,因此除非Web服务支持dataURL格式,否则它可能没有用。 - Christian
这是正确的。您可以通过将其传递给自定义PHP函数,然后使用curl将其发送到Web服务来剥离它们。 - Choy
你觉得这样行吗:var dataURL = canvas.toDataURL("image/png"); var yourImageData = dataURL.replace(/^data:image/(png|jpg);base64,/, ""); - omarojo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接