Permalink
Browse files

File uploads now work

  • Loading branch information...
1 parent 9072890 commit e8c1d6148f6bfe5a76e4e65a27c265c249c49d47 @loopj committed Jun 1, 2011
Showing with 281 additions and 9 deletions.
  1. +98 −9 src/com/loopj/android/http/RequestParams.java
  2. +183 −0 src/com/loopj/android/http/SimpleMultipartEntity.java
@@ -18,6 +18,8 @@
package com.loopj.android.http;
+import java.io.ByteArrayInputStream;
+import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.LinkedList;
import java.util.List;
@@ -32,7 +34,8 @@
public class RequestParams {
private static String ENCODING = "UTF-8";
- protected ConcurrentHashMap<String,String> urlParams;
+ protected ConcurrentHashMap<String, String> stringParams;
+ protected ConcurrentHashMap<String, FileWrapper> fileParams;
public RequestParams() {
init();
@@ -54,39 +57,125 @@ public RequestParams(String key, String value) {
public void put(String key, String value){
if(key != null && value != null) {
- urlParams.put(key,value);
+ stringParams.put(key, value);
+ }
+ }
+
+ public void put(String key, ByteArrayInputStream filedata) {
+ put(key, filedata, null, null);
+ }
+
+ public void put(String key, ByteArrayInputStream filedata, String filename) {
+ put(key, filedata, filename, null);
+ }
+
+ public void put(String key, ByteArrayInputStream filedata, String filename, String contentType){
+ if(key != null && filedata != null) {
+ fileParams.put(key, new FileWrapper(filedata, filename, contentType));
}
}
public void remove(String key){
- urlParams.remove(key);
+ stringParams.remove(key);
+ fileParams.remove(key);
}
public String getParamString() {
+ if(!fileParams.isEmpty()) {
+ throw new RuntimeException("Uploading files is not supported with Http GET requests.");
+ }
+
return URLEncodedUtils.format(getParamsList(), ENCODING);
}
public HttpEntity getEntity() {
HttpEntity entity = null;
- try {
- entity = new UrlEncodedFormEntity(getParamsList(), ENCODING);
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
+
+ if(!fileParams.isEmpty()) {
+ SimpleMultipartEntity multipartEntity = new SimpleMultipartEntity();
+
+ // Add string params
+ for(ConcurrentHashMap.Entry<String, String> entry : stringParams.entrySet()) {
+ multipartEntity.addPart(entry.getKey(), entry.getValue());
+ }
+
+ // Add file params
+ for(ConcurrentHashMap.Entry<String, FileWrapper> entry : fileParams.entrySet()) {
+ FileWrapper file = entry.getValue();
+ if(file.bytes != null) {
+ String filename = file.filename;
+ if(filename == null) {
+ filename = "nofilename";
+ }
+
+ if(file.contentType != null) {
+ multipartEntity.addPart(entry.getKey(), filename, file.bytes, file.contentType);
+ } else {
+ multipartEntity.addPart(entry.getKey(), filename, file.bytes);
+ }
+ }
+ }
+
+ entity = multipartEntity;
+ } else {
+ try {
+ entity = new UrlEncodedFormEntity(getParamsList(), ENCODING);
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
}
+
return entity;
}
private void init(){
- urlParams = new ConcurrentHashMap<String,String>();
+ stringParams = new ConcurrentHashMap<String, String>();
+ fileParams = new ConcurrentHashMap<String, FileWrapper>();
}
private List<BasicNameValuePair> getParamsList() {
List<BasicNameValuePair> lparams = new LinkedList<BasicNameValuePair>();
- for(ConcurrentHashMap.Entry<String, String> entry : urlParams.entrySet()) {
+ for(ConcurrentHashMap.Entry<String, String> entry : stringParams.entrySet()) {
lparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
return lparams;
}
+
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder();
+ for(ConcurrentHashMap.Entry<String, String> entry : stringParams.entrySet()) {
+ if(result.length() > 0)
+ result.append("&");
+
+ result.append(entry.getKey());
+ result.append("=");
+ result.append(entry.getValue());
+ }
+
+ for(ConcurrentHashMap.Entry<String, FileWrapper> entry : fileParams.entrySet()) {
+ if(result.length() > 0)
+ result.append("&");
+
+ result.append(entry.getKey());
+ result.append("=");
+ result.append("FILE");
+ }
+
+ return result.toString();
+ }
+
+ private static class FileWrapper {
+ public ByteArrayInputStream bytes;
+ public String filename;
+ public String contentType;
+
+ public FileWrapper(ByteArrayInputStream bytes, String filename, String contentType) {
+ this.bytes = bytes;
+ this.filename = filename;
+ this.contentType = contentType;
+ }
+ }
}
@@ -0,0 +1,183 @@
+/*
+ Android Asynchronous Http Client
+ Copyright (c) 2011 James Smith <james@loopj.com>
+ http://loopj.com
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/*
+ This code is taken from Rafael Sanches' blog.
+ http://blog.rafaelsanches.com/2011/01/29/upload-using-multipart-post-using-httpclient-in-android/
+*/
+
+package com.loopj.android.http;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Random;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.message.BasicHeader;
+
+public class SimpleMultipartEntity implements HttpEntity {
+ private final static char[] MULTIPART_CHARS = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
+
+ private String boundary = null;
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ boolean isSetLast = false;
+ boolean isSetFirst = false;
+
+ public SimpleMultipartEntity() {
+ final StringBuffer buf = new StringBuffer();
+ final Random rand = new Random();
+ for (int i = 0; i < 30; i++) {
+ buf.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
+ }
+ this.boundary = buf.toString();
+
+ }
+
+ public void writeFirstBoundaryIfNeeds(){
+ if(!isSetFirst){
+ try {
+ out.write(("--" + boundary + "\r\n").getBytes());
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ isSetFirst = true;
+ }
+
+ public void writeLastBoundaryIfNeeds() {
+ if(isSetLast){
+ return;
+ }
+
+ try {
+ out.write(("\r\n--" + boundary + "--\r\n").getBytes());
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+
+ isSetLast = true;
+ }
+
+ public void addPart(final String key, final String value) {
+ writeFirstBoundaryIfNeeds();
+ try {
+ out.write(("Content-Disposition: form-data; name=\"" +key+"\"\r\n\r\n").getBytes());
+ out.write(value.getBytes());
+ out.write(("\r\n--" + boundary + "\r\n").getBytes());
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void addPart(final String key, final String fileName, final InputStream fin){
+ addPart(key, fileName, fin, "application/octet-stream");
+ }
+
+ public void addPart(final String key, final String fileName, final InputStream fin, String type){
+ writeFirstBoundaryIfNeeds();
+ try {
+ type = "Content-Type: "+type+"\r\n";
+ out.write(("Content-Disposition: form-data; name=\""+ key+"\"; filename=\"" + fileName + "\"\r\n").getBytes());
+ out.write(type.getBytes());
+ out.write("Content-Transfer-Encoding: binary\r\n\r\n".getBytes());
+
+ final byte[] tmp = new byte[4096];
+ int l = 0;
+ while ((l = fin.read(tmp)) != -1) {
+ out.write(tmp, 0, l);
+ }
+ out.flush();
+ } catch (final IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ fin.close();
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void addPart(final String key, final File value) {
+ try {
+ addPart(key, value.getName(), new FileInputStream(value));
+ } catch (final FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public long getContentLength() {
+ writeLastBoundaryIfNeeds();
+ return out.toByteArray().length;
+ }
+
+ @Override
+ public Header getContentType() {
+ return new BasicHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
+ }
+
+ @Override
+ public boolean isChunked() {
+ return false;
+ }
+
+ @Override
+ public boolean isRepeatable() {
+ return false;
+ }
+
+ @Override
+ public boolean isStreaming() {
+ return false;
+ }
+
+ @Override
+ public void writeTo(final OutputStream outstream) throws IOException {
+ outstream.write(out.toByteArray());
+ }
+
+ @Override
+ public Header getContentEncoding() {
+ return null;
+ }
+
+ @Override
+ public void consumeContent() throws IOException,
+ UnsupportedOperationException {
+ if (isStreaming()) {
+ throw new UnsupportedOperationException(
+ "Streaming entity does not implement #consumeContent()");
+ }
+ }
+
+ @Override
+ public InputStream getContent() throws IOException,
+ UnsupportedOperationException {
+ return new ByteArrayInputStream(out.toByteArray());
+ }
+}

0 comments on commit e8c1d61

Please sign in to comment.