Skip to content

Commit e8c1d61

Browse files
committed
File uploads now work
1 parent 9072890 commit e8c1d61

File tree

2 files changed

+281
-9
lines changed

2 files changed

+281
-9
lines changed

src/com/loopj/android/http/RequestParams.java

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
package com.loopj.android.http;
2020

21+
import java.io.ByteArrayInputStream;
22+
import java.io.File;
2123
import java.io.UnsupportedEncodingException;
2224
import java.util.LinkedList;
2325
import java.util.List;
@@ -32,7 +34,8 @@
3234
public class RequestParams {
3335
private static String ENCODING = "UTF-8";
3436

35-
protected ConcurrentHashMap<String,String> urlParams;
37+
protected ConcurrentHashMap<String, String> stringParams;
38+
protected ConcurrentHashMap<String, FileWrapper> fileParams;
3639

3740
public RequestParams() {
3841
init();
@@ -54,39 +57,125 @@ public RequestParams(String key, String value) {
5457

5558
public void put(String key, String value){
5659
if(key != null && value != null) {
57-
urlParams.put(key,value);
60+
stringParams.put(key, value);
61+
}
62+
}
63+
64+
public void put(String key, ByteArrayInputStream filedata) {
65+
put(key, filedata, null, null);
66+
}
67+
68+
public void put(String key, ByteArrayInputStream filedata, String filename) {
69+
put(key, filedata, filename, null);
70+
}
71+
72+
public void put(String key, ByteArrayInputStream filedata, String filename, String contentType){
73+
if(key != null && filedata != null) {
74+
fileParams.put(key, new FileWrapper(filedata, filename, contentType));
5875
}
5976
}
6077

6178
public void remove(String key){
62-
urlParams.remove(key);
79+
stringParams.remove(key);
80+
fileParams.remove(key);
6381
}
6482

6583
public String getParamString() {
84+
if(!fileParams.isEmpty()) {
85+
throw new RuntimeException("Uploading files is not supported with Http GET requests.");
86+
}
87+
6688
return URLEncodedUtils.format(getParamsList(), ENCODING);
6789
}
6890

6991
public HttpEntity getEntity() {
7092
HttpEntity entity = null;
71-
try {
72-
entity = new UrlEncodedFormEntity(getParamsList(), ENCODING);
73-
} catch (UnsupportedEncodingException e) {
74-
e.printStackTrace();
93+
94+
if(!fileParams.isEmpty()) {
95+
SimpleMultipartEntity multipartEntity = new SimpleMultipartEntity();
96+
97+
// Add string params
98+
for(ConcurrentHashMap.Entry<String, String> entry : stringParams.entrySet()) {
99+
multipartEntity.addPart(entry.getKey(), entry.getValue());
100+
}
101+
102+
// Add file params
103+
for(ConcurrentHashMap.Entry<String, FileWrapper> entry : fileParams.entrySet()) {
104+
FileWrapper file = entry.getValue();
105+
if(file.bytes != null) {
106+
String filename = file.filename;
107+
if(filename == null) {
108+
filename = "nofilename";
109+
}
110+
111+
if(file.contentType != null) {
112+
multipartEntity.addPart(entry.getKey(), filename, file.bytes, file.contentType);
113+
} else {
114+
multipartEntity.addPart(entry.getKey(), filename, file.bytes);
115+
}
116+
}
117+
}
118+
119+
entity = multipartEntity;
120+
} else {
121+
try {
122+
entity = new UrlEncodedFormEntity(getParamsList(), ENCODING);
123+
} catch (UnsupportedEncodingException e) {
124+
e.printStackTrace();
125+
}
75126
}
127+
76128
return entity;
77129
}
78130

79131
private void init(){
80-
urlParams = new ConcurrentHashMap<String,String>();
132+
stringParams = new ConcurrentHashMap<String, String>();
133+
fileParams = new ConcurrentHashMap<String, FileWrapper>();
81134
}
82135

83136
private List<BasicNameValuePair> getParamsList() {
84137
List<BasicNameValuePair> lparams = new LinkedList<BasicNameValuePair>();
85138

86-
for(ConcurrentHashMap.Entry<String, String> entry : urlParams.entrySet()) {
139+
for(ConcurrentHashMap.Entry<String, String> entry : stringParams.entrySet()) {
87140
lparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
88141
}
89142

90143
return lparams;
91144
}
145+
146+
@Override
147+
public String toString() {
148+
StringBuilder result = new StringBuilder();
149+
for(ConcurrentHashMap.Entry<String, String> entry : stringParams.entrySet()) {
150+
if(result.length() > 0)
151+
result.append("&");
152+
153+
result.append(entry.getKey());
154+
result.append("=");
155+
result.append(entry.getValue());
156+
}
157+
158+
for(ConcurrentHashMap.Entry<String, FileWrapper> entry : fileParams.entrySet()) {
159+
if(result.length() > 0)
160+
result.append("&");
161+
162+
result.append(entry.getKey());
163+
result.append("=");
164+
result.append("FILE");
165+
}
166+
167+
return result.toString();
168+
}
169+
170+
private static class FileWrapper {
171+
public ByteArrayInputStream bytes;
172+
public String filename;
173+
public String contentType;
174+
175+
public FileWrapper(ByteArrayInputStream bytes, String filename, String contentType) {
176+
this.bytes = bytes;
177+
this.filename = filename;
178+
this.contentType = contentType;
179+
}
180+
}
92181
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/*
2+
Android Asynchronous Http Client
3+
Copyright (c) 2011 James Smith <[email protected]>
4+
http://loopj.com
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
*/
18+
19+
/*
20+
This code is taken from Rafael Sanches' blog.
21+
http://blog.rafaelsanches.com/2011/01/29/upload-using-multipart-post-using-httpclient-in-android/
22+
*/
23+
24+
package com.loopj.android.http;
25+
26+
import java.io.ByteArrayInputStream;
27+
import java.io.ByteArrayOutputStream;
28+
import java.io.File;
29+
import java.io.FileInputStream;
30+
import java.io.FileNotFoundException;
31+
import java.io.InputStream;
32+
import java.io.IOException;
33+
import java.io.OutputStream;
34+
import java.util.Random;
35+
36+
import org.apache.http.Header;
37+
import org.apache.http.HttpEntity;
38+
import org.apache.http.message.BasicHeader;
39+
40+
public class SimpleMultipartEntity implements HttpEntity {
41+
private final static char[] MULTIPART_CHARS = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
42+
43+
private String boundary = null;
44+
45+
ByteArrayOutputStream out = new ByteArrayOutputStream();
46+
boolean isSetLast = false;
47+
boolean isSetFirst = false;
48+
49+
public SimpleMultipartEntity() {
50+
final StringBuffer buf = new StringBuffer();
51+
final Random rand = new Random();
52+
for (int i = 0; i < 30; i++) {
53+
buf.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
54+
}
55+
this.boundary = buf.toString();
56+
57+
}
58+
59+
public void writeFirstBoundaryIfNeeds(){
60+
if(!isSetFirst){
61+
try {
62+
out.write(("--" + boundary + "\r\n").getBytes());
63+
} catch (final IOException e) {
64+
e.printStackTrace();
65+
}
66+
}
67+
68+
isSetFirst = true;
69+
}
70+
71+
public void writeLastBoundaryIfNeeds() {
72+
if(isSetLast){
73+
return;
74+
}
75+
76+
try {
77+
out.write(("\r\n--" + boundary + "--\r\n").getBytes());
78+
} catch (final IOException e) {
79+
e.printStackTrace();
80+
}
81+
82+
isSetLast = true;
83+
}
84+
85+
public void addPart(final String key, final String value) {
86+
writeFirstBoundaryIfNeeds();
87+
try {
88+
out.write(("Content-Disposition: form-data; name=\"" +key+"\"\r\n\r\n").getBytes());
89+
out.write(value.getBytes());
90+
out.write(("\r\n--" + boundary + "\r\n").getBytes());
91+
} catch (final IOException e) {
92+
e.printStackTrace();
93+
}
94+
}
95+
96+
public void addPart(final String key, final String fileName, final InputStream fin){
97+
addPart(key, fileName, fin, "application/octet-stream");
98+
}
99+
100+
public void addPart(final String key, final String fileName, final InputStream fin, String type){
101+
writeFirstBoundaryIfNeeds();
102+
try {
103+
type = "Content-Type: "+type+"\r\n";
104+
out.write(("Content-Disposition: form-data; name=\""+ key+"\"; filename=\"" + fileName + "\"\r\n").getBytes());
105+
out.write(type.getBytes());
106+
out.write("Content-Transfer-Encoding: binary\r\n\r\n".getBytes());
107+
108+
final byte[] tmp = new byte[4096];
109+
int l = 0;
110+
while ((l = fin.read(tmp)) != -1) {
111+
out.write(tmp, 0, l);
112+
}
113+
out.flush();
114+
} catch (final IOException e) {
115+
e.printStackTrace();
116+
} finally {
117+
try {
118+
fin.close();
119+
} catch (final IOException e) {
120+
e.printStackTrace();
121+
}
122+
}
123+
}
124+
125+
public void addPart(final String key, final File value) {
126+
try {
127+
addPart(key, value.getName(), new FileInputStream(value));
128+
} catch (final FileNotFoundException e) {
129+
e.printStackTrace();
130+
}
131+
}
132+
133+
@Override
134+
public long getContentLength() {
135+
writeLastBoundaryIfNeeds();
136+
return out.toByteArray().length;
137+
}
138+
139+
@Override
140+
public Header getContentType() {
141+
return new BasicHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
142+
}
143+
144+
@Override
145+
public boolean isChunked() {
146+
return false;
147+
}
148+
149+
@Override
150+
public boolean isRepeatable() {
151+
return false;
152+
}
153+
154+
@Override
155+
public boolean isStreaming() {
156+
return false;
157+
}
158+
159+
@Override
160+
public void writeTo(final OutputStream outstream) throws IOException {
161+
outstream.write(out.toByteArray());
162+
}
163+
164+
@Override
165+
public Header getContentEncoding() {
166+
return null;
167+
}
168+
169+
@Override
170+
public void consumeContent() throws IOException,
171+
UnsupportedOperationException {
172+
if (isStreaming()) {
173+
throw new UnsupportedOperationException(
174+
"Streaming entity does not implement #consumeContent()");
175+
}
176+
}
177+
178+
@Override
179+
public InputStream getContent() throws IOException,
180+
UnsupportedOperationException {
181+
return new ByteArrayInputStream(out.toByteArray());
182+
}
183+
}

0 commit comments

Comments
 (0)