Flex 中不使用 FileReference 上传文件

同样是咔嚓(http://www.kacha.me)中有一个上传文件的功能,因为不是本地文件上传,而是图片的ByteArray,所以无法使用FileReference,之前我使用Scribe Java的类库时候我扩展了一个上传图片的功能,其实,这些都一样,只不过研究下 multipart/form-data 的协议而已。正在打算自己使用 AS3 写一个工具类的时候,我在网上一个博客里找到了一个工具类,省了很多事儿。

使用这种模式上传文件有几个好处:

  • 可以上传非本地文件,比如Flex应用内部的截图,你甚至可以用上一篇文章中所说的生成一个zip文件上传
  • 可以加入附加的参数
  • 可以获得服务器端的返回值

我封装一个文件操作的工具类,里面包括上传,打开本地文件,保存文件到本地等。
直接下载工具类 FileUtils.zip

上传部分代码片段:

public function uploadFile(url:String, data:ByteArray, parameters:Object, onDone:Function=null, onError:Function=null, fileName:String="camix_public.png"):void {
			onDoneCallback=onDone;
			onErrorCallback=onError;
			var request:URLRequest=new URLRequest();
			request.url=url;
			request.method=URLRequestMethod.POST;
			request.contentType='multipart/form-data; boundary=' + UploadPostHelper.getBoundary();
			request.data=UploadPostHelper.getPostData(fileName, data, parameters);

			request.requestHeaders.push(new URLRequestHeader('Cache-Control', 'no-cache'));

			var loader:URLLoader=new URLLoader();
			loader.addEventListener(Event.COMPLETE, onOperationDone);
			loader.addEventListener(IOErrorEvent.IO_ERROR, onSaveError);
			loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
			loader.load(request);
		}

Flex 中不使用 FileReference 上传文件》上有3条评论

  1. skyforce

    您好~
    我也正在尋找scribe上傳圖片的方法.
    如果可以的話, 不知道您方便分享一下做法嗎?

    1. Neal Mi 文章作者

      主要是修改里面的Request类,我把核心的代码贴在下面,剩下的你就可以自己研究了
      [java]
      package org.scribe.model;

      import java.io.*;
      import java.net.*;
      import java.nio.charset.Charset;
      import java.util.*;
      import java.util.concurrent.TimeUnit;

      import org.scribe.exceptions.*;
      import org.scribe.ext.MultiPartFormOutputStream;
      import org.scribe.utils.*;

      /**
      * Represents an HTTP Request object
      *
      * @author Pablo Fernandez
      */
      class Request {
      private static final String CONTENT_LENGTH = “Content-Length”;

      private String url;
      private Verb verb;
      private String encoding = Charset.defaultCharset().name();
      private Map querystringParams;
      private Map
      bodyParams;
      private Map
      headers;
      private Map
      mutilpartMimeTypes;
      private Map
      multipartFields;
      private Map
      multipartFiles;
      private String payload = null;
      private HttpURLConnection connection;

      /**
      * Creates a new Http Request
      *
      * @param verb
      * Http Verb (GET, POST, etc)
      * @param url
      * url with optional querystring parameters.
      */
      public Request(Verb verb, String url) {
      this.verb = verb;
      this.url = url;
      this.querystringParams = new HashMap();
      this.bodyParams = new HashMap
      ();
      this.headers = new HashMap
      ();
      this.multipartFields = new HashMap
      ();
      this.multipartFiles = new HashMap
      ();
      this.mutilpartMimeTypes = new HashMap
      ();
      }

      /**
      * Execute the request and return a {@link Response}
      *
      * @return Http Response
      * @throws RuntimeException
      * if the connection cannot be created.
      */
      public Response send() {
      try {
      createConnection();
      return doSend();
      } catch (IOException ioe) {
      ioe.printStackTrace();
      throw new OAuthException(“Problems while creating connection”, ioe);
      }
      }

      private void createConnection() throws IOException {
      String effectiveUrl = URLUtils.appendParametersToQueryString(url,
      querystringParams);
      if (connection == null) {
      connection = (HttpURLConnection) new URL(effectiveUrl)
      .openConnection();
      }
      }

      Response doSend() throws IOException {
      connection.setRequestMethod(this.verb.name());
      addHeaders(connection);
      if (verb.equals(Verb.POST)
      && (multipartFields.size() != 0 || multipartFiles.size() != 0)) {
      addMultipart(connection);
      } else if (verb.equals(Verb.PUT) || verb.equals(Verb.POST)) {
      addBody(connection, getBodyContents());
      }

      return new Response(connection);
      }

      void addMultipart(URLConnection connection) throws IOException {
      connection.setDoOutput(true);
      String boundary = MultiPartFormOutputStream.createBoundary();

      connection.setRequestProperty(“Accept”, “*/*”);
      connection.setRequestProperty(“Content-Type”,
      MultiPartFormOutputStream.getContentType(boundary));
      // set some other request headers…
      connection.setRequestProperty(“Connection”, “Keep-Alive”);
      connection.setRequestProperty(“Cache-Control”, “no-cache”);
      // no need to connect cuz getOutputStream() does it
      MultiPartFormOutputStream out = new MultiPartFormOutputStream(
      connection.getOutputStream(), boundary);
      // write a text field element
      this.multipartFields.putAll(bodyParams);
      Iterator it = this.multipartFields.keySet().iterator();
      for (; it.hasNext();) {
      String key = it.next();
      String value = this.multipartFields.get(key);
      out.writeField(key, value);
      }
      // upload a file
      Iterator
      ite = this.multipartFiles.keySet().iterator();
      for (; ite.hasNext();) {
      String key = ite.next();
      out.writeFile(key, this.mutilpartMimeTypes.get(key),
      this.multipartFiles.get(key));
      }
      // can also write bytes directly
      // out.writeFile(“myFile”, “text/plain”, “C:\\test.txt”,
      // “This is some file text.”.getBytes(“ASCII”));
      out.close();
      }

      void addHeaders(HttpURLConnection conn) {
      for (String key : headers.keySet())
      conn.setRequestProperty(key, headers.get(key));
      }

      void addBody(HttpURLConnection conn, String content) throws IOException {
      conn.setRequestProperty(CONTENT_LENGTH,
      String.valueOf(content.getBytes(encoding).length));
      conn.setDoOutput(true);
      conn.getOutputStream().write(content.getBytes(encoding));
      }

      /**
      * Add an HTTP Header to the Request
      *
      * @param key
      * the header name
      * @param value
      * the header value
      */
      public void addHeader(String key, String value) {
      this.headers.put(key, value);
      }

      /**
      * Add a body Parameter (for POST/ PUT Requests)
      *
      * @param key
      * the parameter name
      * @param value
      * the parameter value
      */
      public void addBodyParameter(String key, String value) {
      this.bodyParams.put(key, value);
      }

      public void addMultipartField(String key, String value) {
      this.multipartFields.put(key, value);
      }

      public void setEncoding(String value){
      encoding = value;
      }

      public void addMultipartFile(String key, File file) {
      this.multipartFiles.put(key, file);
      }

      public void addMultipartMimeType(String key, String value) {
      this.mutilpartMimeTypes.put(key, value);
      }

      /**
      * Add a QueryString parameter
      *
      * @param key
      * the parameter name
      * @param value
      * the parameter value
      */
      public void addQuerystringParameter(String key, String value) {
      this.querystringParams.put(key, value);
      }

      /**
      * Add body payload.
      *
      * This method is used when the HTTP body is not a form-url-encoded string,
      * but another thing. Like for example XML.
      *
      * Note: The contents are not part of the OAuth signature
      *
      * @param payload
      * the body of the request
      */
      public void addPayload(String payload) {
      this.payload = payload;
      }

      /**
      * Get a {@link Map} of the query string parameters.
      *
      * @return a map containing the query string parameters
      * @throws OAuthException
      * if the URL is not valid
      */
      public Map getQueryStringParams() {
      try {
      Map
      params = new HashMap();
      String queryString = new URL(url).getQuery();
      params.putAll(URLUtils.queryStringToMap(queryString));
      params.putAll(this.querystringParams);
      return params;
      } catch (MalformedURLException mue) {
      throw new OAuthException(“Malformed URL”, mue);
      }
      }

      /**
      * Obtains a {@link Map} of the body parameters.
      *
      * @return a map containing the body parameters.
      */
      public Map getBodyParams() {
      return bodyParams;
      }

      public Map getMultipartParams() {
      return multipartFields;
      }

      /**
      * Obtains the URL of the HTTP Request.
      *
      * @return the original URL of the HTTP Request
      */
      public String getUrl() {
      return url;
      }

      /**
      * Returns the URL without the port and the query string part.
      *
      * @return the OAuth-sanitized URL
      */
      public String getSanitizedUrl() {
      return url.replaceAll(“\\?.*”, “”).replace(“\\:\\d{4}”, “”);
      }

      /**
      * Returns the body of the request
      *
      * @return form encoded string
      */
      public String getBodyContents() {
      return (payload != null) ? payload : URLUtils
      .formURLEncodeMap(bodyParams);
      }

      public String getMultipartContents() {
      return (payload != null) ? payload : URLUtils
      .formURLEncodeMap(multipartFields);
      }

      /**
      * Returns the HTTP Verb
      *
      * @return the verb
      */
      public Verb getVerb() {
      return verb;
      }

      /**
      * Returns the connection headers as a {@link Map}
      *
      * @return map of headers
      */
      public Map getHeaders() {
      return headers;
      }

      /**
      * Sets the connect timeout for the underlying {@link HttpURLConnection}
      *
      * @param duration
      * duration of the timeout
      *
      * @param unit
      * unit of time (milliseconds, seconds, etc)
      */
      public void setConnectTimeout(int duration, TimeUnit unit) {
      this.connection.setConnectTimeout((int) unit.toMillis(duration));
      }

      /**
      * Sets the read timeout for the underlying {@link HttpURLConnection}
      *
      * @param duration
      * duration of the timeout
      *
      * @param unit
      * unit of time (milliseconds, seconds, etc)
      */
      public void setReadTimeout(int duration, TimeUnit unit) {
      this.connection.setReadTimeout((int) unit.toMillis(duration));
      }

      /*
      * We need this in order to stub the connection object for test cases
      */
      void setConnection(HttpURLConnection connection) {
      this.connection = connection;
      }

      @Override
      public String toString() {
      return String.format(“@Request(%s %s)”, getVerb(), getUrl());
      }
      }
      [/java]

      [java]
      package org.scribe.ext;

      import java.io.DataOutputStream;
      import java.io.File;
      import java.io.FileInputStream;
      import java.io.InputStream;
      import java.io.OutputStream;
      import java.net.HttpURLConnection;
      import java.net.URL;
      import java.net.URLConnection;

      /**
      * MultiPartFormOutputStream is used to write “multipart/form-data”
      * to a java.net.URLConnection for POSTing. This is primarily for
      * file uploading to HTTP servers.
      *
      * @since JDK1.3
      */
      public class MultiPartFormOutputStream {
      /**
      * The line end characters.
      */
      private static final String NEWLINE = “\r\n”;

      /**
      * The boundary prefix.
      */
      private static final String PREFIX = “–“;

      /**
      * The output stream to write to.
      */
      private DataOutputStream out = null;

      /**
      * The multipart boundary string.
      */
      private String boundary = null;

      /**
      * Creates a new MultiPartFormOutputStream object using the
      * specified output stream and boundary. The boundary is required to be
      * created before using this method, as described in the description for the
      * getContentType(String) method. The boundary is only checked
      * for null or empty string, but it is recommended to be at
      * least 6 characters. (Or use the static createBoundary() method to create
      * one.)
      *
      * @param os
      * the output stream
      * @param boundary
      * the boundary
      * @see #createBoundary()
      * @see #getContentType(String)
      */
      public MultiPartFormOutputStream(OutputStream os, String boundary) {
      if (os == null) {
      throw new IllegalArgumentException(“Output stream is required.”);
      }
      if (boundary == null || boundary.length() == 0) {
      throw new IllegalArgumentException(“Boundary stream is required.”);
      }
      this.out = new DataOutputStream(os);
      this.boundary = boundary;
      }

      /**
      * Writes an boolean field value.
      *
      * @param name
      * the field name (required)
      * @param value
      * the field value
      * @throws java.io.IOException
      * on input/output errors
      */
      public void writeField(String name, boolean value)
      throws java.io.IOException {
      writeField(name, new Boolean(value).toString());
      }

      /**
      * Writes an double field value.
      *
      * @param name
      * the field name (required)
      * @param value
      * the field value
      * @throws java.io.IOException
      * on input/output errors
      */
      public void writeField(String name, double value)
      throws java.io.IOException {
      writeField(name, Double.toString(value));
      }

      /**
      * Writes an float field value.
      *
      * @param name
      * the field name (required)
      * @param value
      * the field value
      * @throws java.io.IOException
      * on input/output errors
      */
      public void writeField(String name, float value) throws java.io.IOException {
      writeField(name, Float.toString(value));
      }

      /**
      * Writes an long field value.
      *
      * @param name
      * the field name (required)
      * @param value
      * the field value
      * @throws java.io.IOException
      * on input/output errors
      */
      public void writeField(String name, long value) throws java.io.IOException {
      writeField(name, Long.toString(value));
      }

      /**
      * Writes an int field value.
      *
      * @param name
      * the field name (required)
      * @param value
      * the field value
      * @throws java.io.IOException
      * on input/output errors
      */
      public void writeField(String name, int value) throws java.io.IOException {
      writeField(name, Integer.toString(value));
      }

      /**
      * Writes an short field value.
      *
      * @param name
      * the field name (required)
      * @param value
      * the field value
      * @throws java.io.IOException
      * on input/output errors
      */
      public void writeField(String name, short value) throws java.io.IOException {
      writeField(name, Short.toString(value));
      }

      /**
      * Writes an char field value.
      *
      * @param name
      * the field name (required)
      * @param value
      * the field value
      * @throws java.io.IOException
      * on input/output errors
      */
      public void writeField(String name, char value) throws java.io.IOException {
      writeField(name, new Character(value).toString());
      }

      /**
      * Writes an string field value. If the value is null, an empty string is
      * sent (“”).
      *
      * @param name
      * the field name (required)
      * @param value
      * the field value
      * @throws java.io.IOException
      * on input/output errors
      */
      public void writeField(String name, String value)
      throws java.io.IOException {
      if (name == null) {
      throw new IllegalArgumentException(“Name cannot be null or empty.”);
      }
      if (value == null) {
      value = “”;
      }
      /*
      * –boundary\r\n Content-Disposition: form-data; name=”“\r\n
      * \r\n \r\n
      */
      // write boundary
      out.writeBytes(PREFIX);
      out.writeBytes(boundary);
      out.writeBytes(NEWLINE);
      // write content header
      out.writeBytes(“Content-Disposition: form-data; name=\”” + name + “\””);
      out.writeBytes(NEWLINE);
      out.writeBytes(NEWLINE);
      // write content
      // if (“content”.equals(name)){
      byte[] b = value.getBytes(“UTF-8”);
      out.write(b, 0, b.length);
      // out.writeBytes(URLUtils.formURLEncode(value));
      // }else{
      // out.writeBytes(value);
      // }
      // out.writeBytes(value);
      out.writeBytes(NEWLINE);
      out.flush();
      }

      /**
      * Writes a file’s contents. If the file is null, does not exists, or is a
      * directory, a java.lang.IllegalArgumentException will be
      * thrown.
      *
      * @param name
      * the field name
      * @param mimeType
      * the file content type (optional, recommended)
      * @param file
      * the file (the file must exist)
      * @throws java.io.IOException
      * on input/output errors
      */
      public void writeFile(String name, String mimeType, File file)
      throws java.io.IOException {
      if (file == null) {
      throw new IllegalArgumentException(“File cannot be null.”);
      }
      if (!file.exists()) {
      throw new IllegalArgumentException(“File does not exist.”);
      }
      if (file.isDirectory()) {
      throw new IllegalArgumentException(“File cannot be a directory.”);
      }
      writeFile(name, mimeType, file.getCanonicalPath(), new FileInputStream(
      file));
      }

      /**
      * Writes a input stream’s contents. If the input stream is null, a
      * java.lang.IllegalArgumentException will be thrown.
      *
      * @param name
      * the field name
      * @param mimeType
      * the file content type (optional, recommended)
      * @param fileName
      * the file name (required)
      * @param is
      * the input stream
      * @throws java.io.IOException
      * on input/output errors
      */
      public void writeFile(String name, String mimeType, String fileName,
      InputStream is) throws java.io.IOException {
      if (is == null) {
      throw new IllegalArgumentException(“Input stream cannot be null.”);
      }
      if (fileName == null || fileName.length() == 0) {
      throw new IllegalArgumentException(
      “File name cannot be null or empty.”);
      }
      /*
      * –boundary\r\n Content-Disposition: form-data; name=”“;
      * filename=”“\r\n Content-Type: \r\n \r\n
      * \r\n
      */
      // write boundary
      out.writeBytes(PREFIX);
      out.writeBytes(boundary);
      out.writeBytes(NEWLINE);
      // write content header
      out.writeBytes(“Content-Disposition: form-data; name=\”” + name
      + “\”; filename=\”” + fileName + “\””);
      out.writeBytes(NEWLINE);
      if (mimeType != null) {
      out.writeBytes(“Content-Type: ” + mimeType);
      out.writeBytes(NEWLINE);
      }
      out.writeBytes(NEWLINE);
      // write content
      byte[] data = new byte[1024];
      int r = 0;
      while ((r = is.read(data, 0, data.length)) != -1) {
      out.write(data, 0, r);
      }
      // close input stream, but ignore any possible exception for it
      try {
      is.close();
      } catch (Exception e) {
      }
      out.writeBytes(NEWLINE);
      out.flush();
      }

      /**
      * Writes the given bytes. The bytes are assumed to be the contents of a
      * file, and will be sent as such. If the data is null, a
      * java.lang.IllegalArgumentException will be thrown.
      *
      * @param name
      * the field name
      * @param mimeType
      * the file content type (optional, recommended)
      * @param fileName
      * the file name (required)
      * @param data
      * the file data
      * @throws java.io.IOException
      * on input/output errors
      */
      public void writeFile(String name, String mimeType, String fileName,
      byte[] data) throws java.io.IOException {
      if (data == null) {
      throw new IllegalArgumentException(“Data cannot be null.”);
      }
      if (fileName == null || fileName.length() == 0) {
      throw new IllegalArgumentException(
      “File name cannot be null or empty.”);
      }
      /*
      * –boundary\r\n Content-Disposition: form-data; name=”“;
      * filename=”“\r\n Content-Type: \r\n \r\n
      * \r\n
      */
      // write boundary
      out.writeBytes(PREFIX);
      out.writeBytes(boundary);
      out.writeBytes(NEWLINE);
      // write content header
      out.writeBytes(“Content-Disposition: form-data; name=\”” + name
      + “\”; filename=\”” + fileName + “\””);
      out.writeBytes(NEWLINE);
      if (mimeType != null) {
      out.writeBytes(“Content-Type: ” + mimeType);
      out.writeBytes(NEWLINE);
      }
      out.writeBytes(NEWLINE);
      // write content
      out.write(data, 0, data.length);
      out.writeBytes(NEWLINE);
      out.flush();
      }

      /**
      * Flushes the stream. Actually, this method does nothing, as the only write
      * methods are highly specialized and automatically flush.
      *
      * @throws java.io.IOException
      * on input/output errors
      */
      public void flush() throws java.io.IOException {
      // out.flush();
      }

      /**
      * Closes the stream.
      *
      * NOTE: This method MUST be called to finalize the multipart
      * stream.
      *
      * @throws java.io.IOException
      * on input/output errors
      */
      public void close() throws java.io.IOException {
      // write final boundary
      out.writeBytes(PREFIX);
      out.writeBytes(boundary);
      out.writeBytes(PREFIX);
      out.writeBytes(NEWLINE);
      out.flush();
      out.close();
      }

      /**
      * Gets the multipart boundary string being used by this stream.
      *
      * @return the boundary
      */
      public String getBoundary() {
      return this.boundary;
      }

      /**
      * Creates a new java.net.URLConnection object from the
      * specified java.net.URL. This is a convenience method which
      * will set the doInput, doOutput,
      * useCaches and defaultUseCaches fields to the
      * appropriate settings in the correct order.
      *
      * @return a java.net.URLConnection object for the URL
      * @throws java.io.IOException
      * on input/output errors
      */
      public static URLConnection createConnection(URL url)
      throws java.io.IOException {
      URLConnection urlConn = url.openConnection();
      if (urlConn instanceof HttpURLConnection) {
      HttpURLConnection httpConn = (HttpURLConnection) urlConn;
      httpConn.setRequestMethod(“POST”);
      }
      urlConn.setDoInput(true);
      urlConn.setDoOutput(true);
      urlConn.setUseCaches(false);
      urlConn.setDefaultUseCaches(false);
      return urlConn;
      }

      /**
      * Creates a multipart boundary string by concatenating 20 hyphens (-) and
      * the hexadecimal (base-16) representation of the current time in
      * milliseconds.
      *
      * @return a multipart boundary string
      * @see #getContentType(String)
      */
      public static String createBoundary() {
      return “——————–”
      + Long.toString(System.currentTimeMillis(), 16);
      }

      /**
      * Gets the content type string suitable for the
      * java.net.URLConnection which includes the multipart boundary
      * string.
      *
      * This method is static because, due to the nature of the
      * java.net.URLConnection class, once the output stream for the
      * connection is acquired, it’s too late to set the content type (or any
      * other request parameter). So one has to create a multipart boundary
      * string first before using this class, such as with the
      * createBoundary() method.
      *
      * @param boundary
      * the boundary string
      * @return the content type string
      * @see #createBoundary()
      */
      public static String getContentType(String boundary) {
      return “multipart/form-data; boundary=” + boundary;
      }
      }

      [/java]

评论已关闭。