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