【问题标题】:image upload to web service in JavaScript在 JavaScript 中将图像上传到 Web 服务
【发布时间】:2011-12-31 23:54:05
【问题描述】:

我需要从 javascript 将图像上传到网络服务。我必须发送一个 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,该网络服务正在使用 Java,它是一个 Restful 网络服务。网络服务是由某人编写的。我只需要访问。我无法改变它。

标签: javascript image file-upload


【解决方案1】:

假设您的 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 APIXMLHttpRequestXMLHttpRequest2 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 不成功)。

² 如果无法访问属性,则会抛出异常。如果您更喜欢真正的测试,请实施(附加)功能测试(而不是),并注意并非所有内容都可以安全地进行功能测试。

【讨论】:

    【解决方案2】:

    对于上传图片,我使用Valum's ajax upload pluginjQuery form plugin 允许以ajax 方式提交普通表单。

    如果您将使用 POST 请求,请不要忘记使用 MAX_FILE_SIZE 隐藏属性:

    &lt;input type="hidden" name="MAX_FILE_SIZE" value="20000000"&gt;

    请注意,它必须位于文件输入字段之前。它以字节为单位,因此这会将上传限制为 20MB。详情请见PHP documentation

    【讨论】:

    • @kommradHomer,我只在 Opera 上使用它,对于其他浏览器我使用 Valum 的插件 - 主要优点是不需要表单 - 只需单击即可添加图像。
    【解决方案3】:

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

    首先,给你的页面添加一个隐藏的画布:

    $("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
    });  
    

    【讨论】:

    • 这会发送一个 base64 编码的 png 文件,该文件前面有一些额外的标题,所以它可能没用,除非 web 服务支持 dataURL 格式。
    • 确实如此。您可以通过将其传递给自定义 php 函数,然后使用 curl 将其发送到 Web 服务来删除它们。
    • 这样做怎么样: var dataURL = canvas.toDataURL("image/png"); var yourImageData = dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
    【解决方案4】:

    MultipartEntity 听起来像Multipart/form-data

    您可以使用普通的XMLHttpRequest 发出 POST 请求。您可以使用 HTML 5 FormData 来构建您的 Multipart/form-data 请求。

    这里是一个例子:HTML5 File API readAsBinaryString reads files as much larger, different than files on disk

    【讨论】:

      【解决方案5】:

      您实际上可以使用 javascript 调用服务,here 有一个示例代码

      如果您的要求是上传图片并从 JS 调用 web 服务,那么这可能会很棘手。

      您可以简单地将图像上传到服务器并让服务器调用网络服务,有很多工具可以帮助您upload a file to a server

      【讨论】:

      • Anantha Sharma,我有一个由 Java 中的某个人编写的 Web 服务和一个 Restfull Web 服务。我必须使用 javascript 将图像和 json 消息从 html 页面直接上传到 web 服务(我不能使用任何服务器端脚本)。我需要使用 javascript 将上传的图像和 json 消息作为多个部分(MultipartEntity)发送。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-07
      • 1970-01-01
      • 1970-01-01
      • 2018-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多