Part.java revision 823675fdbb7f974b8e2fa9fbb71774b32487582d
1/* 2 * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/Part.java,v 1.16 2005/01/14 21:16:40 olegk Exp $ 3 * $Revision: 480424 $ 4 * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $ 5 * 6 * ==================================================================== 7 * 8 * Licensed to the Apache Software Foundation (ASF) under one or more 9 * contributor license agreements. See the NOTICE file distributed with 10 * this work for additional information regarding copyright ownership. 11 * The ASF licenses this file to You under the Apache License, Version 2.0 12 * (the "License"); you may not use this file except in compliance with 13 * the License. You may obtain a copy of the License at 14 * 15 * http://www.apache.org/licenses/LICENSE-2.0 16 * 17 * Unless required by applicable law or agreed to in writing, software 18 * distributed under the License is distributed on an "AS IS" BASIS, 19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 * See the License for the specific language governing permissions and 21 * limitations under the License. 22 * ==================================================================== 23 * 24 * This software consists of voluntary contributions made by many 25 * individuals on behalf of the Apache Software Foundation. For more 26 * information on the Apache Software Foundation, please see 27 * <http://www.apache.org/>. 28 * 29 */ 30 31package com.android.internal.http.multipart; 32 33import java.io.ByteArrayOutputStream; 34import java.io.IOException; 35import java.io.OutputStream; 36 37import org.apache.http.util.EncodingUtils; 38import org.apache.commons.logging.Log; 39import org.apache.commons.logging.LogFactory; 40 41/** 42 * Abstract class for one Part of a multipart post object. 43 * 44 * @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a> 45 * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a> 46 * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a> 47 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a> 48 * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a> 49 * 50 * @since 2.0 51 * 52 * @deprecated Please use {@link java.net.URLConnection} and friends instead. 53 * The Apache HTTP client is no longer maintained and may be removed in a future 54 * release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> 55 * for further details. 56 */ 57@Deprecated 58public abstract class Part { 59 60 /** Log object for this class. */ 61 private static final Log LOG = LogFactory.getLog(Part.class); 62 63 /** 64 * The boundary 65 * @deprecated use {@link org.apache.http.client.methods.multipart#MULTIPART_BOUNDARY} 66 */ 67 protected static final String BOUNDARY = "----------------314159265358979323846"; 68 69 /** 70 * The boundary as a byte array. 71 * @deprecated 72 */ 73 protected static final byte[] BOUNDARY_BYTES = EncodingUtils.getAsciiBytes(BOUNDARY); 74 75 /** 76 * The default boundary to be used if {@link #setPartBoundary(byte[])} has not 77 * been called. 78 */ 79 private static final byte[] DEFAULT_BOUNDARY_BYTES = BOUNDARY_BYTES; 80 81 /** Carriage return/linefeed */ 82 protected static final String CRLF = "\r\n"; 83 84 /** Carriage return/linefeed as a byte array */ 85 protected static final byte[] CRLF_BYTES = EncodingUtils.getAsciiBytes(CRLF); 86 87 /** Content dispostion characters */ 88 protected static final String QUOTE = "\""; 89 90 /** Content dispostion as a byte array */ 91 protected static final byte[] QUOTE_BYTES = 92 EncodingUtils.getAsciiBytes(QUOTE); 93 94 /** Extra characters */ 95 protected static final String EXTRA = "--"; 96 97 /** Extra characters as a byte array */ 98 protected static final byte[] EXTRA_BYTES = 99 EncodingUtils.getAsciiBytes(EXTRA); 100 101 /** Content dispostion characters */ 102 protected static final String CONTENT_DISPOSITION = "Content-Disposition: form-data; name="; 103 104 /** Content dispostion as a byte array */ 105 protected static final byte[] CONTENT_DISPOSITION_BYTES = 106 EncodingUtils.getAsciiBytes(CONTENT_DISPOSITION); 107 108 /** Content type header */ 109 protected static final String CONTENT_TYPE = "Content-Type: "; 110 111 /** Content type header as a byte array */ 112 protected static final byte[] CONTENT_TYPE_BYTES = 113 EncodingUtils.getAsciiBytes(CONTENT_TYPE); 114 115 /** Content charset */ 116 protected static final String CHARSET = "; charset="; 117 118 /** Content charset as a byte array */ 119 protected static final byte[] CHARSET_BYTES = 120 EncodingUtils.getAsciiBytes(CHARSET); 121 122 /** Content type header */ 123 protected static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding: "; 124 125 /** Content type header as a byte array */ 126 protected static final byte[] CONTENT_TRANSFER_ENCODING_BYTES = 127 EncodingUtils.getAsciiBytes(CONTENT_TRANSFER_ENCODING); 128 129 /** 130 * Return the boundary string. 131 * @return the boundary string 132 * @deprecated uses a constant string. Rather use {@link #getPartBoundary} 133 */ 134 public static String getBoundary() { 135 return BOUNDARY; 136 } 137 138 /** 139 * The ASCII bytes to use as the multipart boundary. 140 */ 141 private byte[] boundaryBytes; 142 143 /** 144 * Return the name of this part. 145 * @return The name. 146 */ 147 public abstract String getName(); 148 149 /** 150 * Returns the content type of this part. 151 * @return the content type, or <code>null</code> to exclude the content type header 152 */ 153 public abstract String getContentType(); 154 155 /** 156 * Return the character encoding of this part. 157 * @return the character encoding, or <code>null</code> to exclude the character 158 * encoding header 159 */ 160 public abstract String getCharSet(); 161 162 /** 163 * Return the transfer encoding of this part. 164 * @return the transfer encoding, or <code>null</code> to exclude the transfer encoding header 165 */ 166 public abstract String getTransferEncoding(); 167 168 /** 169 * Gets the part boundary to be used. 170 * @return the part boundary as an array of bytes. 171 * 172 * @since 3.0 173 */ 174 protected byte[] getPartBoundary() { 175 if (boundaryBytes == null) { 176 // custom boundary bytes have not been set, use the default. 177 return DEFAULT_BOUNDARY_BYTES; 178 } else { 179 return boundaryBytes; 180 } 181 } 182 183 /** 184 * Sets the part boundary. Only meant to be used by 185 * {@link Part#sendParts(OutputStream, Part[], byte[])} 186 * and {@link Part#getLengthOfParts(Part[], byte[])} 187 * @param boundaryBytes An array of ASCII bytes. 188 * @since 3.0 189 */ 190 void setPartBoundary(byte[] boundaryBytes) { 191 this.boundaryBytes = boundaryBytes; 192 } 193 194 /** 195 * Tests if this part can be sent more than once. 196 * @return <code>true</code> if {@link #sendData(OutputStream)} can be successfully called 197 * more than once. 198 * @since 3.0 199 */ 200 public boolean isRepeatable() { 201 return true; 202 } 203 204 /** 205 * Write the start to the specified output stream 206 * @param out The output stream 207 * @throws IOException If an IO problem occurs. 208 */ 209 protected void sendStart(OutputStream out) throws IOException { 210 LOG.trace("enter sendStart(OutputStream out)"); 211 out.write(EXTRA_BYTES); 212 out.write(getPartBoundary()); 213 out.write(CRLF_BYTES); 214 } 215 216 /** 217 * Write the content disposition header to the specified output stream 218 * 219 * @param out The output stream 220 * @throws IOException If an IO problem occurs. 221 */ 222 protected void sendDispositionHeader(OutputStream out) throws IOException { 223 LOG.trace("enter sendDispositionHeader(OutputStream out)"); 224 out.write(CONTENT_DISPOSITION_BYTES); 225 out.write(QUOTE_BYTES); 226 out.write(EncodingUtils.getAsciiBytes(getName())); 227 out.write(QUOTE_BYTES); 228 } 229 230 /** 231 * Write the content type header to the specified output stream 232 * @param out The output stream 233 * @throws IOException If an IO problem occurs. 234 */ 235 protected void sendContentTypeHeader(OutputStream out) throws IOException { 236 LOG.trace("enter sendContentTypeHeader(OutputStream out)"); 237 String contentType = getContentType(); 238 if (contentType != null) { 239 out.write(CRLF_BYTES); 240 out.write(CONTENT_TYPE_BYTES); 241 out.write(EncodingUtils.getAsciiBytes(contentType)); 242 String charSet = getCharSet(); 243 if (charSet != null) { 244 out.write(CHARSET_BYTES); 245 out.write(EncodingUtils.getAsciiBytes(charSet)); 246 } 247 } 248 } 249 250 /** 251 * Write the content transfer encoding header to the specified 252 * output stream 253 * 254 * @param out The output stream 255 * @throws IOException If an IO problem occurs. 256 */ 257 protected void sendTransferEncodingHeader(OutputStream out) throws IOException { 258 LOG.trace("enter sendTransferEncodingHeader(OutputStream out)"); 259 String transferEncoding = getTransferEncoding(); 260 if (transferEncoding != null) { 261 out.write(CRLF_BYTES); 262 out.write(CONTENT_TRANSFER_ENCODING_BYTES); 263 out.write(EncodingUtils.getAsciiBytes(transferEncoding)); 264 } 265 } 266 267 /** 268 * Write the end of the header to the output stream 269 * @param out The output stream 270 * @throws IOException If an IO problem occurs. 271 */ 272 protected void sendEndOfHeader(OutputStream out) throws IOException { 273 LOG.trace("enter sendEndOfHeader(OutputStream out)"); 274 out.write(CRLF_BYTES); 275 out.write(CRLF_BYTES); 276 } 277 278 /** 279 * Write the data to the specified output stream 280 * @param out The output stream 281 * @throws IOException If an IO problem occurs. 282 */ 283 protected abstract void sendData(OutputStream out) throws IOException; 284 285 /** 286 * Return the length of the main content 287 * 288 * @return long The length. 289 * @throws IOException If an IO problem occurs 290 */ 291 protected abstract long lengthOfData() throws IOException; 292 293 /** 294 * Write the end data to the output stream. 295 * @param out The output stream 296 * @throws IOException If an IO problem occurs. 297 */ 298 protected void sendEnd(OutputStream out) throws IOException { 299 LOG.trace("enter sendEnd(OutputStream out)"); 300 out.write(CRLF_BYTES); 301 } 302 303 /** 304 * Write all the data to the output stream. 305 * If you override this method make sure to override 306 * #length() as well 307 * 308 * @param out The output stream 309 * @throws IOException If an IO problem occurs. 310 */ 311 public void send(OutputStream out) throws IOException { 312 LOG.trace("enter send(OutputStream out)"); 313 sendStart(out); 314 sendDispositionHeader(out); 315 sendContentTypeHeader(out); 316 sendTransferEncodingHeader(out); 317 sendEndOfHeader(out); 318 sendData(out); 319 sendEnd(out); 320 } 321 322 323 /** 324 * Return the full length of all the data. 325 * If you override this method make sure to override 326 * #send(OutputStream) as well 327 * 328 * @return long The length. 329 * @throws IOException If an IO problem occurs 330 */ 331 public long length() throws IOException { 332 LOG.trace("enter length()"); 333 if (lengthOfData() < 0) { 334 return -1; 335 } 336 ByteArrayOutputStream overhead = new ByteArrayOutputStream(); 337 sendStart(overhead); 338 sendDispositionHeader(overhead); 339 sendContentTypeHeader(overhead); 340 sendTransferEncodingHeader(overhead); 341 sendEndOfHeader(overhead); 342 sendEnd(overhead); 343 return overhead.size() + lengthOfData(); 344 } 345 346 /** 347 * Return a string representation of this object. 348 * @return A string representation of this object. 349 * @see java.lang.Object#toString() 350 */ 351 @Override 352 public String toString() { 353 return this.getName(); 354 } 355 356 /** 357 * Write all parts and the last boundary to the specified output stream. 358 * 359 * @param out The stream to write to. 360 * @param parts The parts to write. 361 * 362 * @throws IOException If an I/O error occurs while writing the parts. 363 */ 364 public static void sendParts(OutputStream out, final Part[] parts) 365 throws IOException { 366 sendParts(out, parts, DEFAULT_BOUNDARY_BYTES); 367 } 368 369 /** 370 * Write all parts and the last boundary to the specified output stream. 371 * 372 * @param out The stream to write to. 373 * @param parts The parts to write. 374 * @param partBoundary The ASCII bytes to use as the part boundary. 375 * 376 * @throws IOException If an I/O error occurs while writing the parts. 377 * 378 * @since 3.0 379 */ 380 public static void sendParts(OutputStream out, Part[] parts, byte[] partBoundary) 381 throws IOException { 382 383 if (parts == null) { 384 throw new IllegalArgumentException("Parts may not be null"); 385 } 386 if (partBoundary == null || partBoundary.length == 0) { 387 throw new IllegalArgumentException("partBoundary may not be empty"); 388 } 389 for (int i = 0; i < parts.length; i++) { 390 // set the part boundary before the part is sent 391 parts[i].setPartBoundary(partBoundary); 392 parts[i].send(out); 393 } 394 out.write(EXTRA_BYTES); 395 out.write(partBoundary); 396 out.write(EXTRA_BYTES); 397 out.write(CRLF_BYTES); 398 } 399 400 /** 401 * Return the total sum of all parts and that of the last boundary 402 * 403 * @param parts The parts. 404 * @return The total length 405 * 406 * @throws IOException If an I/O error occurs while writing the parts. 407 */ 408 public static long getLengthOfParts(Part[] parts) 409 throws IOException { 410 return getLengthOfParts(parts, DEFAULT_BOUNDARY_BYTES); 411 } 412 413 /** 414 * Gets the length of the multipart message including the given parts. 415 * 416 * @param parts The parts. 417 * @param partBoundary The ASCII bytes to use as the part boundary. 418 * @return The total length 419 * 420 * @throws IOException If an I/O error occurs while writing the parts. 421 * 422 * @since 3.0 423 */ 424 public static long getLengthOfParts(Part[] parts, byte[] partBoundary) throws IOException { 425 LOG.trace("getLengthOfParts(Parts[])"); 426 if (parts == null) { 427 throw new IllegalArgumentException("Parts may not be null"); 428 } 429 long total = 0; 430 for (int i = 0; i < parts.length; i++) { 431 // set the part boundary before we calculate the part's length 432 parts[i].setPartBoundary(partBoundary); 433 long l = parts[i].length(); 434 if (l < 0) { 435 return -1; 436 } 437 total += l; 438 } 439 total += EXTRA_BYTES.length; 440 total += partBoundary.length; 441 total += EXTRA_BYTES.length; 442 total += CRLF_BYTES.length; 443 return total; 444 } 445} 446