1 /* 2 * ==================================================================== 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * ==================================================================== 20 * 21 * This software consists of voluntary contributions made by many 22 * individuals on behalf of the Apache Software Foundation. For more 23 * information on the Apache Software Foundation, please see 24 * <http://www.apache.org/>. 25 * 26 */ 27 28 package ch.boye.httpclientandroidlib.entity.mime; 29 30 import java.io.File; 31 import java.io.InputStream; 32 import java.nio.charset.Charset; 33 import java.util.ArrayList; 34 import java.util.Collections; 35 import java.util.List; 36 import java.util.Random; 37 38 import ch.boye.httpclientandroidlib.HttpEntity; 39 import ch.boye.httpclientandroidlib.entity.ContentType; 40 import ch.boye.httpclientandroidlib.entity.mime.content.ByteArrayBody; 41 import ch.boye.httpclientandroidlib.entity.mime.content.ContentBody; 42 import ch.boye.httpclientandroidlib.entity.mime.content.FileBody; 43 import ch.boye.httpclientandroidlib.entity.mime.content.InputStreamBody; 44 import ch.boye.httpclientandroidlib.entity.mime.content.StringBody; 45 import ch.boye.httpclientandroidlib.util.Args; 46 47 /** 48 * Builder for multipart {@link HttpEntity}s. 49 * 50 * @since 4.3 51 */ 52 public class MultipartEntityBuilder { 53 54 /** 55 * The pool of ASCII chars to be used for generating a multipart boundary. 56 */ 57 private final static char[] MULTIPART_CHARS = 58 "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 59 .toCharArray(); 60 61 private final static String DEFAULT_SUBTYPE = "form-data"; 62 63 private String subType = DEFAULT_SUBTYPE; 64 private HttpMultipartMode mode = HttpMultipartMode.STRICT; 65 private String boundary = null; 66 private Charset charset = null; 67 private List<FormBodyPart> bodyParts = null; 68 create()69 public static MultipartEntityBuilder create() { 70 return new MultipartEntityBuilder(); 71 } 72 MultipartEntityBuilder()73 MultipartEntityBuilder() { 74 super(); 75 } 76 setMode(final HttpMultipartMode mode)77 public MultipartEntityBuilder setMode(final HttpMultipartMode mode) { 78 this.mode = mode; 79 return this; 80 } 81 setLaxMode()82 public MultipartEntityBuilder setLaxMode() { 83 this.mode = HttpMultipartMode.BROWSER_COMPATIBLE; 84 return this; 85 } 86 setStrictMode()87 public MultipartEntityBuilder setStrictMode() { 88 this.mode = HttpMultipartMode.STRICT; 89 return this; 90 } 91 setBoundary(final String boundary)92 public MultipartEntityBuilder setBoundary(final String boundary) { 93 this.boundary = boundary; 94 return this; 95 } 96 setCharset(final Charset charset)97 public MultipartEntityBuilder setCharset(final Charset charset) { 98 this.charset = charset; 99 return this; 100 } 101 addPart(final FormBodyPart bodyPart)102 MultipartEntityBuilder addPart(final FormBodyPart bodyPart) { 103 if (bodyPart == null) { 104 return this; 105 } 106 if (this.bodyParts == null) { 107 this.bodyParts = new ArrayList<FormBodyPart>(); 108 } 109 this.bodyParts.add(bodyPart); 110 return this; 111 } 112 addPart(final String name, final ContentBody contentBody)113 public MultipartEntityBuilder addPart(final String name, final ContentBody contentBody) { 114 Args.notNull(name, "Name"); 115 Args.notNull(contentBody, "Content body"); 116 return addPart(new FormBodyPart(name, contentBody)); 117 } 118 addTextBody( final String name, final String text, final ContentType contentType)119 public MultipartEntityBuilder addTextBody( 120 final String name, final String text, final ContentType contentType) { 121 return addPart(name, new StringBody(text, contentType)); 122 } 123 addTextBody( final String name, final String text)124 public MultipartEntityBuilder addTextBody( 125 final String name, final String text) { 126 return addTextBody(name, text, ContentType.DEFAULT_TEXT); 127 } 128 addBinaryBody( final String name, final byte[] b, final ContentType contentType, final String filename)129 public MultipartEntityBuilder addBinaryBody( 130 final String name, final byte[] b, final ContentType contentType, final String filename) { 131 return addPart(name, new ByteArrayBody(b, contentType, filename)); 132 } 133 addBinaryBody( final String name, final byte[] b)134 public MultipartEntityBuilder addBinaryBody( 135 final String name, final byte[] b) { 136 return addBinaryBody(name, b, ContentType.DEFAULT_BINARY, null); 137 } 138 addBinaryBody( final String name, final File file, final ContentType contentType, final String filename)139 public MultipartEntityBuilder addBinaryBody( 140 final String name, final File file, final ContentType contentType, final String filename) { 141 return addPart(name, new FileBody(file, contentType, filename)); 142 } 143 addBinaryBody( final String name, final File file)144 public MultipartEntityBuilder addBinaryBody( 145 final String name, final File file) { 146 return addBinaryBody(name, file, ContentType.DEFAULT_BINARY, file != null ? file.getName() : null); 147 } 148 addBinaryBody( final String name, final InputStream stream, final ContentType contentType, final String filename)149 public MultipartEntityBuilder addBinaryBody( 150 final String name, final InputStream stream, final ContentType contentType, 151 final String filename) { 152 return addPart(name, new InputStreamBody(stream, contentType, filename)); 153 } 154 addBinaryBody(final String name, final InputStream stream)155 public MultipartEntityBuilder addBinaryBody(final String name, final InputStream stream) { 156 return addBinaryBody(name, stream, ContentType.DEFAULT_BINARY, null); 157 } 158 generateContentType( final String boundary, final Charset charset)159 private String generateContentType( 160 final String boundary, 161 final Charset charset) { 162 final StringBuilder buffer = new StringBuilder(); 163 buffer.append("multipart/form-data; boundary="); 164 buffer.append(boundary); 165 if (charset != null) { 166 buffer.append("; charset="); 167 buffer.append(charset.name()); 168 } 169 return buffer.toString(); 170 } 171 generateBoundary()172 private String generateBoundary() { 173 final StringBuilder buffer = new StringBuilder(); 174 final Random rand = new Random(); 175 final int count = rand.nextInt(11) + 30; // a random size from 30 to 40 176 for (int i = 0; i < count; i++) { 177 buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]); 178 } 179 return buffer.toString(); 180 } 181 buildEntity()182 MultipartFormEntity buildEntity() { 183 final String st = subType != null ? subType : DEFAULT_SUBTYPE; 184 final Charset cs = charset; 185 final String b = boundary != null ? boundary : generateBoundary(); 186 final List<FormBodyPart> bps = bodyParts != null ? new ArrayList<FormBodyPart>(bodyParts) : 187 Collections.<FormBodyPart>emptyList(); 188 final HttpMultipartMode m = mode != null ? mode : HttpMultipartMode.STRICT; 189 final AbstractMultipartForm form; 190 switch (m) { 191 case BROWSER_COMPATIBLE: 192 form = new HttpBrowserCompatibleMultipart(st, cs, b, bps); 193 break; 194 case RFC6532: 195 form = new HttpRFC6532Multipart(st, cs, b, bps); 196 break; 197 default: 198 form = new HttpStrictMultipart(st, cs, b, bps); 199 } 200 return new MultipartFormEntity(form, generateContentType(b, cs), form.getTotalLength()); 201 } 202 build()203 public HttpEntity build() { 204 return buildEntity(); 205 } 206 207 } 208