你的位置:首页 > 操作系统

[操作系统]使用Volley上传文件


使用浏览器上传文件,然后通过Wireshark抓包分析,发现发送的数据大概是这个样子。

MIME Multipart Media Encapsulation, Type: multipart/form-data, Boundary: "----WebKitFormBoundary1UBMMKIkN58civN4"  [Type: multipart/form-data]  First boundary: ------WebKitFormBoundary1UBMMKIkN58civN4\r\n  Encapsulated multipart part:     Content-Disposition: form-data; name="name"\r\n\r\n    Data (16 bytes)  Boundary: \r\n------WebKitFormBoundary1UBMMKIkN58civN4\r\n  Encapsulated multipart part: (image/png)    Content-Disposition: form-data; name="photo[]"; filename="Screenshot (2).png"\r\n    Content-Type: image/png\r\n\r\n    Portable Network Graphics  Boundary: \r\n------WebKitFormBoundary1UBMMKIkN58civN4\r\n  Encapsulated multipart part: (image/png)  Boundary: \r\n------WebKitFormBoundary1UBMMKIkN58civN4\r\n  Encapsulated multipart part: (image/png)  Boundary: \r\n------WebKitFormBoundary1UBMMKIkN58civN4\r\n  Encapsulated multipart part: (image/png)  Boundary: \r\n------WebKitFormBoundary1UBMMKIkN58civN4\r\n  Encapsulated multipart part: (image/png)  Last boundary: \r\n------WebKitFormBoundary1UBMMKIkN58civN4--\r\n

 首先来自定义一个HttpEntity,

package cc.dewdrop.volleydemo.utils;import com.android.volley.VolleyLog;import org.apache.http.Header;import org.apache.http.HttpEntity;import org.apache.http.message.BasicHeader;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.Closeable;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import javax.activation.MimetypesFileTypeMap;/** * Created by Tingkuo on 12/1/2015. */public class FileUploadEntity implements HttpEntity {  private static final String TAG = FileUploadEntity.class.getSimpleName();  private static final String BOUNDARY = "__FILE_UPLOAD_ENTITY__";  private ByteArrayOutputStream mOutputStream;  public FileUploadEntity() {    mOutputStream = new ByteArrayOutputStream();    try {      writeFirstBoundary();    } catch (IOException e) {      e.printStackTrace();    }  }  private void writeFirstBoundary() throws IOException {    VolleyLog.e("writeFirstBoundary");    mOutputStream.write(("--" + BOUNDARY + "\r\n").getBytes());    mOutputStream.write(("Content-Disposition: form-data; name=\"" + "name" + "\"\r\n\r\n").getBytes());    mOutputStream.write("Content-Transfer-Encoding: binary\n\n".getBytes());    mOutputStream.flush();  }  private void writeLastBoundary() throws IOException {    VolleyLog.e("writeLastBoundary");    mOutputStream.write(("\r\n--" + BOUNDARY + "--\r\n").getBytes());  }  public void addFile(final String key, final File file) {    VolleyLog.e("addFile");    InputStream inputStream = null;    try {      mOutputStream.write(("\r\n--" + BOUNDARY + "\r\n").getBytes());      StringBuilder stringBuilderContentDisposition = new StringBuilder();      stringBuilderContentDisposition.append("Content-Disposition: ");      stringBuilderContentDisposition.append("form-data; ");      stringBuilderContentDisposition.append("name=\"" + key + "\"; ");      stringBuilderContentDisposition.append("filename=\"" + file.getName() + "\"\r\n");      mOutputStream.write(stringBuilderContentDisposition.toString().getBytes());      StringBuilder stringBuilderContentType = new StringBuilder();      stringBuilderContentType.append("Content-Type: ");      stringBuilderContentType.append(new MimetypesFileTypeMap().getContentType(file).toString());      stringBuilderContentType.append("\r\n\r\n");      mOutputStream.write(stringBuilderContentType.toString().getBytes());      inputStream = new FileInputStream(file);      final byte[] buffer = new byte[1024];      int len = 0;      while ((len = inputStream.read(buffer)) != -1) {        VolleyLog.e("len --> %s", String.valueOf(len));        mOutputStream.write(buffer, 0, len);      }      VolleyLog.e("===last====len --> %s", String.valueOf(len));      mOutputStream.flush();    } catch (FileNotFoundException e) {      e.printStackTrace();    } catch (IOException e) {      e.printStackTrace();    } finally {      closeSilently(inputStream);    }  }  private void closeSilently(Closeable closeable) {    try {      if (closeable != null) {        closeable.close();      }    } catch (final IOException e) {      e.printStackTrace();    }  }  @Override  public boolean isRepeatable() {    return false;  }  @Override  public boolean isChunked() {    return false;  }  @Override  public long getContentLength() {    return mOutputStream.toByteArray().length;  }  @Override  public Header getContentType() {    return new BasicHeader("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);  }  @Override  public Header getContentEncoding() {    return null;  }  @Override  public InputStream getContent() throws IOException, UnsupportedOperationException {    return new ByteArrayInputStream(mOutputStream.toByteArray());  }  @Override  public void writeTo(OutputStream outputStream) throws IOException {    writeLastBoundary();    outputStream.write(mOutputStream.toByteArray());  }  @Override  public boolean isStreaming() {    return false;  }  @Override  public void consumeContent() throws IOException {  }}

现在来解释一下,首先这是支持多文件上传的,数据格式一共包括四部分,Content-Type,First boundary,文件二进制数据[],及Last boundary。可以有多个文件,使用addFile方法插入,文件之间需要有分隔符Boundary。每个文件需要有Content-Disposition及Content-Type

 

然后再自定义一个Request,根据需要使用不同的构造方法

package cc.dewdrop.volleydemo.utils;import com.android.volley.AuthFailureError;import com.android.volley.NetworkResponse;import com.android.volley.Request;import com.android.volley.Response;import com.android.volley.Response.Listener;import com.android.volley.Response.ErrorListener;import com.android.volley.toolbox.HttpHeaderParser;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.UnsupportedEncodingException;import java.util.HashMap;import java.util.Map;/** * Created by Tingkuo on 12/2/2015. */public class FileUploadRequest extends Request<String> {  private final Listener<String> mListener;  private FileUploadEntity mFileUploadEntity = new FileUploadEntity();  private Map<String, String> mHeaders = new HashMap<>();  public FileUploadRequest(String url, Listener<String> listener) {    this(url, listener, null);  }  public FileUploadRequest(String url, Listener<String> listener, ErrorListener errorListener) {    this(Method.POST, url, listener, errorListener);  }  public FileUploadRequest(int method, String url, Listener<String> listener, ErrorListener errorListener) {    super(method, url, errorListener);    this.mListener = listener;  }  public FileUploadEntity getFileUploadEntity() {    return mFileUploadEntity;  }  @Override  public String getBodyContentType() {    return mFileUploadEntity.getContentType().getValue();  }  public void addHeader(String key, String value) {    mHeaders.put(key, value);  }  @Override  public Map<String, String> getHeaders() throws AuthFailureError {    return mHeaders;  }  @Override  public byte[] getBody() throws AuthFailureError {    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();    try {      mFileUploadEntity.writeTo(outputStream);    } catch (IOException e) {      e.printStackTrace();    }    return outputStream.toByteArray();  }  @Override  protected Response<String> parseNetworkResponse(NetworkResponse response) {    String parsed = "";    try {      parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));    } catch (UnsupportedEncodingException e) {      parsed = new String(response.data);    }    return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));  }  @Override  protected void deliverResponse(String response) {    if (mListener != null) {      mListener.onResponse(response);    }  }}

代码是放在Volley中其他类型Request来写的,没什么好说的。

最后就是如何调用

  private void simpleUploadFile() {    File file = new File(Environment.getExternalStorageDirectory().getPath() + "/upload.png");    fileUploadRequest = new FileUploadRequest(        Request.Method.POST,        urlList.get(2),        new Response.Listener<String>() {          @Override          public void onResponse(String response) {            textViewInfo.setText("Post Succeed:\n" + response.replace("<br>", "\n"));            Log.e(TAG, response);            dialog.dismiss();          }        },        new Response.ErrorListener() {          @Override          public void onErrorResponse(VolleyError error) {            textViewInfo.setText("Post Failed:\n" + error.getMessage());            Log.e(TAG, error.getMessage());            dialog.dismiss();          }        }    );    fileUploadRequest.addHeader("User-Agent", "Android 5.1.1");    FileUploadEntity fileUploadEntity = fileUploadRequest.getFileUploadEntity();    fileUploadEntity.addFile("file[]", file);    fileUploadEntity.addFile("file[]", file);    fileUploadEntity.addFile("file[]", file);    fileUploadEntity.addFile("file[]", file);    fileUploadEntity.addFile("file[]", file);    requestQueue.add(fileUploadRequest);    dialog.show();  }

实例化一个新的Request对象,传入Method,Url,然后通过Request对象来获取Entity,通过addFile()方法来传入需要上传的文件,最后加入requestQueue,使用方法与其他类型Request相同。

 

备注:

需要添加以下依赖:

  compile 'org.apache.httpcomponents:httpcore:4.4.4'  compile 'org.apache.httpcomponents:httpmime:4.5.1'  compile files('libs/volley.jar')  compile files('libs/activation.jar')