1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.http.entity.mime;
29
30 import java.io.ByteArrayOutputStream;
31 import java.io.IOException;
32 import java.io.OutputStream;
33 import java.nio.ByteBuffer;
34 import java.nio.CharBuffer;
35 import java.nio.charset.Charset;
36 import java.util.ArrayList;
37 import java.util.List;
38
39 import org.apache.http.entity.mime.content.ContentBody;
40 import org.apache.http.util.ByteArrayBuffer;
41
42
43
44
45
46
47
48
49 public class HttpMultipart {
50
51 private static ByteArrayBuffer encode(
52 final Charset charset, final String string) {
53 ByteBuffer encoded = charset.encode(CharBuffer.wrap(string));
54 ByteArrayBuffer bab = new ByteArrayBuffer(encoded.remaining());
55 bab.append(encoded.array(), encoded.position(), encoded.remaining());
56 return bab;
57 }
58
59 private static void writeBytes(
60 final ByteArrayBuffer b, final OutputStream out) throws IOException {
61 out.write(b.buffer(), 0, b.length());
62 }
63
64 private static void writeBytes(
65 final String s, final Charset charset, final OutputStream out) throws IOException {
66 ByteArrayBuffer b = encode(charset, s);
67 writeBytes(b, out);
68 }
69
70 private static void writeBytes(
71 final String s, final OutputStream out) throws IOException {
72 ByteArrayBuffer b = encode(MIME.DEFAULT_CHARSET, s);
73 writeBytes(b, out);
74 }
75
76 private static void writeField(
77 final MinimalField field, final OutputStream out) throws IOException {
78 writeBytes(field.getName(), out);
79 writeBytes(FIELD_SEP, out);
80 writeBytes(field.getBody(), out);
81 writeBytes(CR_LF, out);
82 }
83
84 private static void writeField(
85 final MinimalField field, final Charset charset, final OutputStream out) throws IOException {
86 writeBytes(field.getName(), charset, out);
87 writeBytes(FIELD_SEP, out);
88 writeBytes(field.getBody(), charset, out);
89 writeBytes(CR_LF, out);
90 }
91
92 private static final ByteArrayBuffer FIELD_SEP = encode(MIME.DEFAULT_CHARSET, ": ");
93 private static final ByteArrayBuffer CR_LF = encode(MIME.DEFAULT_CHARSET, "\r\n");
94 private static final ByteArrayBuffer TWO_DASHES = encode(MIME.DEFAULT_CHARSET, "--");
95
96
97 private final String subType;
98 private final Charset charset;
99 private final String boundary;
100 private final List<FormBodyPart> parts;
101
102 private final HttpMultipartMode mode;
103
104
105
106
107
108
109
110
111
112
113 public HttpMultipart(final String subType, final Charset charset, final String boundary, HttpMultipartMode mode) {
114 super();
115 if (subType == null) {
116 throw new IllegalArgumentException("Multipart subtype may not be null");
117 }
118 if (boundary == null) {
119 throw new IllegalArgumentException("Multipart boundary may not be null");
120 }
121 this.subType = subType;
122 this.charset = charset != null ? charset : MIME.DEFAULT_CHARSET;
123 this.boundary = boundary;
124 this.parts = new ArrayList<FormBodyPart>();
125 this.mode = mode;
126 }
127
128
129
130
131
132
133
134
135
136
137 public HttpMultipart(final String subType, final Charset charset, final String boundary) {
138 this(subType, charset, boundary, HttpMultipartMode.STRICT);
139 }
140
141 public HttpMultipart(final String subType, final String boundary) {
142 this(subType, null, boundary);
143 }
144
145 public String getSubType() {
146 return this.subType;
147 }
148
149 public Charset getCharset() {
150 return this.charset;
151 }
152
153 public HttpMultipartMode getMode() {
154 return this.mode;
155 }
156
157 public List<FormBodyPart> getBodyParts() {
158 return this.parts;
159 }
160
161 public void addBodyPart(final FormBodyPart part) {
162 if (part == null) {
163 return;
164 }
165 this.parts.add(part);
166 }
167
168 public String getBoundary() {
169 return this.boundary;
170 }
171
172 private void doWriteTo(
173 final HttpMultipartMode mode,
174 final OutputStream out,
175 boolean writeContent) throws IOException {
176
177 ByteArrayBuffer boundary = encode(this.charset, getBoundary());
178 for (FormBodyPart part: this.parts) {
179 writeBytes(TWO_DASHES, out);
180 writeBytes(boundary, out);
181 writeBytes(CR_LF, out);
182
183 Header header = part.getHeader();
184
185 switch (mode) {
186 case STRICT:
187 for (MinimalField field: header) {
188 writeField(field, out);
189 }
190 break;
191 case BROWSER_COMPATIBLE:
192
193
194 MinimalField cd = part.getHeader().getField(MIME.CONTENT_DISPOSITION);
195 writeField(cd, this.charset, out);
196 String filename = part.getBody().getFilename();
197 if (filename != null) {
198 MinimalField ct = part.getHeader().getField(MIME.CONTENT_TYPE);
199 writeField(ct, this.charset, out);
200 }
201 break;
202 }
203 writeBytes(CR_LF, out);
204
205 if (writeContent) {
206 part.getBody().writeTo(out);
207 }
208 writeBytes(CR_LF, out);
209 }
210 writeBytes(TWO_DASHES, out);
211 writeBytes(boundary, out);
212 writeBytes(TWO_DASHES, out);
213 writeBytes(CR_LF, out);
214 }
215
216
217
218
219
220
221
222
223 public void writeTo(final OutputStream out) throws IOException {
224 doWriteTo(this.mode, out, true);
225 }
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240 public long getTotalLength() {
241 long contentLen = 0;
242 for (FormBodyPart part: this.parts) {
243 ContentBody body = part.getBody();
244 long len = body.getContentLength();
245 if (len >= 0) {
246 contentLen += len;
247 } else {
248 return -1;
249 }
250 }
251 ByteArrayOutputStream out = new ByteArrayOutputStream();
252 try {
253 doWriteTo(this.mode, out, false);
254 byte[] extra = out.toByteArray();
255 return contentLen + extra.length;
256 } catch (IOException ex) {
257
258 return -1;
259 }
260 }
261
262 }