1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.jclouds.io.payloads;
20
21 import static com.google.common.base.Throwables.propagate;
22 import static com.google.common.collect.Lists.newArrayList;
23 import static com.google.common.io.ByteStreams.join;
24 import static com.google.common.io.ByteStreams.newInputStreamSupplier;
25
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.util.Map.Entry;
29
30 import com.google.common.io.InputSupplier;
31
32
33
34
35
36 public class MultipartForm extends BasePayload<Iterable<? extends Part>> {
37 public static final String BOUNDARY = "--JCLOUDS--";
38 private static final String rn = "\r\n";
39 private static final String dd = "--";
40
41 private boolean isRepeatable;
42 private final InputSupplier<? extends InputStream> chain;
43
44 @SuppressWarnings("unchecked")
45 public MultipartForm(String boundary, Iterable<? extends Part> content) {
46 super(content);
47 getContentMetadata().setContentType("multipart/form-data; boundary=" + boundary);
48 getContentMetadata().setContentLength(0l);
49 String boundaryrn = boundary + rn;
50 isRepeatable = true;
51 InputSupplier<? extends InputStream> chain = join();
52 for (Part part : content) {
53 if (!part.isRepeatable())
54 isRepeatable = false;
55 getContentMetadata().setContentLength(
56 getContentMetadata().getContentLength() + part.getContentMetadata().getContentLength());
57 chain = join(chain, addLengthAndReturnHeaders(boundaryrn, part), part, addLengthAndReturnRn());
58 }
59 chain = join(chain, addLengthAndReturnFooter(boundary));
60 this.chain = chain;
61 }
62
63 public MultipartForm(String boundary, Part... parts) {
64 this(boundary, newArrayList(parts));
65 }
66
67 public MultipartForm(Part... parts) {
68 this(BOUNDARY, parts);
69 }
70
71 private InputSupplier<? extends InputStream> addLengthAndReturnRn() {
72 getContentMetadata().setContentLength(getContentMetadata().getContentLength() + rn.length());
73 return newInputStreamSupplier(rn.getBytes());
74 }
75
76 private InputSupplier<? extends InputStream> addLengthAndReturnHeaders(String boundaryrn, Part part) {
77 StringBuilder builder = new StringBuilder(dd).append(boundaryrn);
78 for (Entry<String, String> entry : part.getHeaders().entries()) {
79 String header = String.format("%s: %s%s", entry.getKey(), entry.getValue(), rn);
80 builder.append(header);
81 }
82 builder.append(rn);
83 getContentMetadata().setContentLength(getContentMetadata().getContentLength() + builder.length());
84 return newInputStreamSupplier(builder.toString().getBytes());
85 }
86
87 private InputSupplier<? extends InputStream> addLengthAndReturnFooter(String boundary) {
88 String end = dd + boundary + dd + rn;
89 getContentMetadata().setContentLength(getContentMetadata().getContentLength() + end.length());
90 return newInputStreamSupplier(end.getBytes());
91 }
92
93 @Override
94 public InputStream getInput() {
95 try {
96 return chain.getInput();
97 } catch (IOException e) {
98 propagate(e);
99 return null;
100 }
101 }
102
103 @Override
104 public boolean isRepeatable() {
105 return isRepeatable;
106 }
107
108 @Override
109 public void release() {
110 for (Part part : content)
111 part.release();
112 }
113
114 }