/* * Copyright 2010 Ning, Inc. * * Ning licenses this file to you under the Apache License, version 2.0 * (the "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.ning.http.multipart; import com.ning.http.client.FluentStringsMap; import java.io.IOException; import java.io.OutputStream; import java.util.Random; /** * This class is an adaptation of the Apache HttpClient implementation * * @link http://hc.apache.org/httpclient-3.x/ */ public class MultipartRequestEntity implements RequestEntity { /** * The Content-Type for multipart/form-data. */ private static final String MULTIPART_FORM_CONTENT_TYPE = "multipart/form-data"; /** * The pool of ASCII chars to be used for generating a multipart boundary. */ private static byte[] MULTIPART_CHARS = MultipartEncodingUtil.getAsciiBytes( "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); /** * Generates a random multipart boundary string. * * @return */ private static byte[] generateMultipartBoundary() { Random rand = new Random(); byte[] bytes = new byte[rand.nextInt(11) + 30]; // a random size from 30 to 40 for (int i = 0; i < bytes.length; i++) { bytes[i] = MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]; } return bytes; } /** * The MIME parts as set by the constructor */ protected Part[] parts; private byte[] multipartBoundary; private FluentStringsMap methodParams; /** * Creates a new multipart entity containing the given parts. * * @param parts The parts to include. * @param methodParams The params of the HttpMethod using this entity. */ public MultipartRequestEntity(Part[] parts, FluentStringsMap methodParams) { if (parts == null) { throw new IllegalArgumentException("parts cannot be null"); } if (methodParams == null) { methodParams = new FluentStringsMap(); } this.parts = parts; this.methodParams = methodParams; } /** * Returns the MIME boundary string that is used to demarcate boundaries of * this part. The first call to this method will implicitly create a new * boundary string. To create a boundary string first the * HttpMethodParams.MULTIPART_BOUNDARY parameter is considered. Otherwise * a random one is generated. * * @return The boundary string of this entity in ASCII encoding. */ protected byte[] getMultipartBoundary() { if (multipartBoundary == null) { String temp = methodParams.get("") == null ? null : methodParams.get("").iterator().next(); if (temp != null) { multipartBoundary = MultipartEncodingUtil.getAsciiBytes(temp); } else { multipartBoundary = generateMultipartBoundary(); } } return multipartBoundary; } /** * Returns <code>true</code> if all parts are repeatable, <code>false</code> otherwise. */ public boolean isRepeatable() { for (int i = 0; i < parts.length; i++) { if (!parts[i].isRepeatable()) { return false; } } return true; } /* (non-Javadoc) * @see org.apache.commons.httpclient.methods.RequestEntity#writeRequest(java.io.OutputStream) */ public void writeRequest(OutputStream out) throws IOException { Part.sendParts(out, parts, getMultipartBoundary()); } /* (non-Javadoc) * @see org.apache.commons.httpclient.methods.RequestEntity#getContentLength() */ public long getContentLength() { try { return Part.getLengthOfParts(parts, getMultipartBoundary()); } catch (Exception e) { return 0; } } /* (non-Javadoc) * @see org.apache.commons.httpclient.methods.RequestEntity#getContentType() */ public String getContentType() { StringBuffer buffer = new StringBuffer(MULTIPART_FORM_CONTENT_TYPE); buffer.append("; boundary="); buffer.append(MultipartEncodingUtil.getAsciiString(getMultipartBoundary())); return buffer.toString(); } }