/* See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* Esri Inc. 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.esri.gpt.agp.multipart2;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
/**
* A part of an HTTP multi-part request.
* <p/>
* Based upon Apache HttpClient 3.1:
* org.apache.commons.httpclient.methods.multipart
*/
public class MPart {
/** instance variables ====================================================== */
private String boundary;
private String charset;
private String contentType;
private byte[] data;
private String fileName;
private String name;
private String transferEncoding;
/** constructors ============================================================ */
/** Default constructor. */
public MPart() {}
/**
* Constructor.
* @param name the part name
* @param data the data
*/
public MPart(String name, String data) {
this(name,data,"text/plain");
}
/**
* Constructor.
* @param name the part name
* @param data the data
* @param contentType the content type
*/
public MPart(String name, String data, String contentType) {
this.setName(name);
try {
this.setData(data.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
// shouldn't happen
e.printStackTrace();
}
this.setFileName(fileName);
if (contentType == null) contentType = "text/plain";
this.setContentType(contentType);
this.setCharset("UTF-8");
//this.setTransferEncoding("8bit");
//this.setTransferEncoding("binary");
}
/**
* Constructor.
* @param name the part name
* @param data the data
* @param fileName the file name
* @param contentType the content type
* @param charset the character set
*/
public MPart(String name,
byte[] data,
String fileName,
String contentType,
String charset) {
this.setName(name);
this.setData(data);
this.setFileName(fileName);
if (contentType == null) contentType = "application/octet-stream";
this.setContentType(contentType);
this.setCharset(charset);
this.setTransferEncoding("binary");
}
/** properties ============================================================== */
/**
* Gets the boundary string.
* @return the boundary
*/
public String getBoundary() {
return this.boundary;
}
/**
* Sets the boundary string.
* @param boundary the boundary
*/
public void setBoundary(String boundary) {
this.boundary = boundary;
}
/**
* Gets the character set.
* @return the character set
*/
public String getCharset() {
return this.charset;
}
/**
* Sets the character set.
* @param charset the character set
*/
public void setCharset(String charset) {
this.charset = charset;
}
/**
* Gets the content type.
* @return the content type
*/
public String getContentType() {
return this.contentType;
}
/**
* Sets the content type.
* @param contentType the content type
*/
public void setContentType(String contentType) {
this.contentType = contentType;
}
/**
* Gets the data.
* @return the data
*/
public byte[] getData() {
return this.data;
}
/**
* Sets the data.
* @param data the data
*/
public void setData(byte[] data) {
this.data = data;
}
/**
* Gets the file name.
* @return the file name
*/
public String getFileName() {
return this.fileName;
}
/**
* Sets the file name.
* @param fileName the file name
*/
public void setFileName(String fileName) {
this.fileName = fileName;
}
/**
* Gets the part name.
* @return the part name
*/
public String getName() {
return this.name;
}
/**
* Sets the part name.
* @param name the part name
*/
public void setName(String name) {
this.name = name;
}
/**
* Gets the transfer encoding.
* @return the transfer encoding
*/
public String getTransferEncoding() {
return this.transferEncoding;
}
/**
* Sets the transfer encoding.
* @param transferEncoding the transfer encoding
*/
public void setTransferEncoding(String transferEncoding) {
this.transferEncoding = transferEncoding;
}
/** methods ================================================================= */
/**
* Determine the length of the part data in bytes (-1 if unknown).
* <br/>This should be overridden if the data is not a string or byte array.
* @return the data length
* @throws IOException if an exception occurs
*/
protected long dataLength() throws IOException {
if (this.data == null) {
return 0;
} else {
return this.data.length;
}
}
/**
* Gets the US-ASCII bytes for a value
* @param value the value
* @return the bytes
* @throws UnsupportedEncodingException if the encoding is unsupported
*/
protected byte[] getAsciiBytes(String value) throws UnsupportedEncodingException {
return value.getBytes("US-ASCII");
}
/**
* True if the part can be written to the output stream more than once.
* @return true if repeatable
*/
public boolean isRepeatable() {
return true;
}
/**
* Determine the length of the part in bytes (-1 if unknown).
* @return the length of the part
* @throws IOException if an exception occurs
*/
public long partLength() throws IOException {
if (dataLength() < 0) {
return -1;
}
ByteArrayOutputStream overhead = new ByteArrayOutputStream();
sendStart(overhead);
sendDispositionHeader(overhead);
sendContentTypeHeader(overhead);
sendTransferEncodingHeader(overhead);
sendEndOfHeader(overhead);
sendEnd(overhead);
//System.err.println(new String(overhead.toByteArray()));
return overhead.size() + dataLength();
}
/**
* Write the part to the stream.
* @param out the output stream
* @throws IOException if an exception occurs
*/
public void send(OutputStream out) throws IOException {
sendStart(out);
sendDispositionHeader(out);
sendContentTypeHeader(out);
sendTransferEncodingHeader(out);
sendEndOfHeader(out);
sendData(out);
sendEnd(out);
}
/**
* Write the Content-Type to the stream.
* @param out the output stream
* @throws IOException if an exception occurs
*/
protected void sendContentTypeHeader(OutputStream out) throws IOException {
String sContentType = this.getContentType();
String sCharset = this.getCharset();
if (sContentType != null) {
out.write(this.getAsciiBytes("\r\n"));;
out.write(this.getAsciiBytes("Content-Type: "));
out.write(this.getAsciiBytes(sContentType));
if (sCharset != null) {
out.write(this.getAsciiBytes("; charset="));
out.write(this.getAsciiBytes(sCharset));
}
}
}
/**
* Write the part data to the stream.
* @param out the output stream
* @throws IOException if an exception occurs
*/
protected void sendData(OutputStream out) throws IOException {
if (this.dataLength() > 0) {
out.write(this.getData());
//System.err.println(new String(this.getData(),"UTF-8"));
}
}
/**
* Write the Content-Disposition to the stream.
* @param out the output stream
* @throws IOException if an exception occurs
*/
protected void sendDispositionHeader(OutputStream out) throws IOException {
out.write(this.getAsciiBytes("Content-Disposition: form-data; name="));
out.write(this.getAsciiBytes("\""));
out.write(this.getAsciiBytes(this.getName()));
out.write(this.getAsciiBytes("\""));
String sFileName = this.getFileName();
if (sFileName != null) {
out.write(this.getAsciiBytes("; filename="));
out.write(this.getAsciiBytes("\""));
out.write(this.getAsciiBytes(sFileName));
out.write(this.getAsciiBytes("\""));
}
}
/**
* Write the bytes that end a part to the stream.
* @param out the output stream
* @throws IOException if an exception occurs
*/
protected void sendEnd(OutputStream out) throws IOException {
out.write(this.getAsciiBytes("\r\n"));
}
/**
* Write the bytes that end a part header to the stream.
* @param out the output stream
* @throws IOException if an exception occurs
*/
protected void sendEndOfHeader(OutputStream out) throws IOException {
out.write(this.getAsciiBytes("\r\n"));
out.write(this.getAsciiBytes("\r\n"));
}
/**
* Write the bytes that start a part to the stream.
* @param out the output stream
* @throws IOException if an exception occurs
*/
protected void sendStart(OutputStream out) throws IOException {
out.write(this.getAsciiBytes("--"));
out.write(this.getAsciiBytes(this.getBoundary()));
out.write(this.getAsciiBytes("\r\n"));
}
/**
* Write the content Content-Transfer-Encoding to the stream.
* @param out the output stream
* @throws IOException if an exception occurs
*/
protected void sendTransferEncodingHeader(OutputStream out) throws IOException {
String sTransferEncoding = this.getTransferEncoding();
if (sTransferEncoding != null) {
out.write(this.getAsciiBytes("\r\n"));
out.write(this.getAsciiBytes("Content-Transfer-Encoding: "));
out.write(this.getAsciiBytes(sTransferEncoding));
}
}
/**
* Stream data from an input to an output.
* @param source the input stream
* @param destination the output stream
* @throws IOException if an exception occurs
*/
protected long streamData(InputStream source, OutputStream destination) throws IOException {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
byte buffer[] = new byte[4096];
int nRead = 0;
int nMax = buffer.length;
long nTotal = 0;
bis = new BufferedInputStream(source);
bos = new BufferedOutputStream(destination);
while ((nRead = bis.read(buffer,0,nMax)) >= 0) {
bos.write(buffer,0,nRead);
nTotal += nRead;
}
return nTotal;
} finally {
try {if (bos != null) bos.flush();} catch (Exception ef) {}
}
}
}