/**
* Copyright (c) 2012-2013, Michael Yang 杨福海 (www.yangfuhai.com).
*
* Licensed 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 net.tsz.afinal.http;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.message.BasicHeader;
/**
* 参考 http://blog.rafaelsanches.com/2011/01/29/upload-using-multipart-post-
* using-httpclient-in-android/ 拿来主义... :)
* @author michael yang (www.yangfuhai.com)
*/
class MultipartEntity implements HttpEntity {
private final static char[] MULTIPART_CHARS = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
private String boundary = null;
ByteArrayOutputStream out = new ByteArrayOutputStream();
boolean isSetLast = false;
boolean isSetFirst = false;
public MultipartEntity() {
final StringBuffer buf = new StringBuffer();
final Random rand = new Random();
for (int i = 0; i < 30; i++) {
buf.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
}
this.boundary = buf.toString();
}
public void writeFirstBoundaryIfNeeds(){
if(!isSetFirst){
try {
out.write(("--" + boundary + "\r\n").getBytes());
} catch (final IOException e) {
e.printStackTrace();
}
}
isSetFirst = true;
}
public void writeLastBoundaryIfNeeds() {
if(isSetLast){
return;
}
try {
out.write(("\r\n--" + boundary + "--\r\n").getBytes());
} catch (final IOException e) {
e.printStackTrace();
}
isSetLast = true;
}
public void addPart(final String key, final String value) {
writeFirstBoundaryIfNeeds();
try {
out.write(("Content-Disposition: form-data; name=\"" +key+"\"\r\n\r\n").getBytes());
out.write(value.getBytes());
out.write(("\r\n--" + boundary + "\r\n").getBytes());
} catch (final IOException e) {
e.printStackTrace();
}
}
public void addPart(final String key, final String fileName, final InputStream fin, final boolean isLast){
addPart(key, fileName, fin, "application/octet-stream", isLast);
}
public void addPart(final String key, final String fileName, final InputStream fin, String type, final boolean isLast){
writeFirstBoundaryIfNeeds();
try {
type = "Content-Type: "+type+"\r\n";
out.write(("Content-Disposition: form-data; name=\""+ key+"\"; filename=\"" + fileName + "\"\r\n").getBytes());
out.write(type.getBytes());
out.write("Content-Transfer-Encoding: binary\r\n\r\n".getBytes());
final byte[] tmp = new byte[4096];
int l = 0;
while ((l = fin.read(tmp)) != -1) {
out.write(tmp, 0, l);
}
if(!isLast)
out.write(("\r\n--" + boundary + "\r\n").getBytes());
out.flush();
} catch (final IOException e) {
e.printStackTrace();
} finally {
try {
fin.close();
} catch (final IOException e) {
e.printStackTrace();
}
}
}
public void addPart(final String key, final File value, final boolean isLast) {
try {
addPart(key, value.getName(), new FileInputStream(value), isLast);
} catch (final FileNotFoundException e) {
e.printStackTrace();
}
}
@Override
public long getContentLength() {
writeLastBoundaryIfNeeds();
return out.toByteArray().length;
}
@Override
public Header getContentType() {
return new BasicHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
}
@Override
public boolean isChunked() {
return false;
}
@Override
public boolean isRepeatable() {
return false;
}
@Override
public boolean isStreaming() {
return false;
}
@Override
public void writeTo(final OutputStream outstream) throws IOException {
outstream.write(out.toByteArray());
}
@Override
public Header getContentEncoding() {
return null;
}
@Override
public void consumeContent() throws IOException,
UnsupportedOperationException {
if (isStreaming()) {
throw new UnsupportedOperationException(
"Streaming entity does not implement #consumeContent()");
}
}
@Override
public InputStream getContent() throws IOException,
UnsupportedOperationException {
return new ByteArrayInputStream(out.toByteArray());
}
}