I am using the google-http-client for java to handle http connections on Android.
Everything is HunkyDori with it thus far until trying to send a multipart/form-data
Content.
I am a little bit stuck, I am thinking about adapting the MultipartContent
class to create my own version of MultipartFormContent
.
I need a version of MultipartEntity
which would effectivly work like:
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(url);
FileBody bin = new FileBody(new File(fileName));
StringBody comment = new StringBody("Filename: " + fileName);
MultipartEntity reqEntity = new MultipartEntity();
reqEntity.addPart("bin", bin);
reqEntity.addPart("comment", comment);
httppost.setEntity(reqEntity);
HttpResponse response = httpclient.execute(httppost);
HttpEntity resEntity = response.getEntity();
From reading around, the only difference between, multipart/relative
and multipart/form
is that one adds the name when printing out?
Anyone got any ideas about adapting the MultipartContent
class to support the 'name' field.
Regards.
Ok, I created the following class based off of the MultipartContent class:
/**
* Serializes MIME multipart content to the form-data standard. Adapated from {@link MultipartContent}
* <p/>
* <p>
* By default the media type is {@code "multipart/form-data; boundary=__END_OF_PART__"}, but this may
* be customized by calling {@link #setMediaType(HttpMediaType)}, {@link #getMediaType()}, or
* {@link #setBoundary(String)}.
* </p>
* <p/>
* <p>
* Implementation is not thread-safe.
* </p>
*
* @author Christopher Jenkins
* @since 1.15
*/
public class MultipartFormContent extends AbstractHttpContent
{
static final String NEWLINE = "\r\n";
private static final String TWO_DASHES = "--";
/**
* Parts of the HTTP multipart request.
*/
private ArrayList<Part> parts = new ArrayList<Part>();
public MultipartFormContent()
{
super(new HttpMediaType("multipart/form-data").setParameter("boundary", "__END_OF_PART__"));
}
public void writeTo(OutputStream out) throws IOException
{
Writer writer = new OutputStreamWriter(out, getCharset());
String boundary = getBoundary();
//TODO use http headers again like MultipartContent
HttpHeaders headers;
String contentDisposition = null;
HttpContent content = null;
StreamingContent streamingContent = null;
for (Part part : parts)
{
// analyze the headers
headers = new HttpHeaders().setAcceptEncoding(null);
if (part.headers != null)
headers.fromHttpHeaders(part.headers);
headers.setContentEncoding(null)
.setUserAgent(null)
.setContentType(null)
.setContentLength(null)
.set("Content-Transfer-Encoding", null);
// Write disposition
if (part.getName() != null)
{
contentDisposition = String.format("form-data; name=\"%s\"", part.name);
// Do we have a filename?
// Then add to the content dispos
if (part.filename != null)
contentDisposition += String.format("; filename=\"%s\"", part.filename);
headers.set("Content-Disposition", contentDisposition);
}
// analyze the content
content = part.content;
streamingContent = null;
if (content != null)
{
headers.setContentType(content.getType());
headers.set("Content-Transfer-Encoding", Arrays.asList("binary"));
final HttpEncoding encoding = part.encoding;
if (encoding == null)
streamingContent = content;
else
{
headers.setContentEncoding(encoding.getName());
streamingContent = new HttpEncodingStreamingContent(content, encoding);
}
}
// write separator
writer.write(TWO_DASHES);
writer.write(boundary);
writer.write(NEWLINE);
// Write Headers
HttpHeaders.serializeHeadersForMultipartRequests(headers, null, null, writer);
// write content
if (streamingContent != null)
{
writer.write(NEWLINE);
writer.flush();
streamingContent.writeTo(out);
writer.write(NEWLINE);
}
}
// write end separator
writer.write(TWO_DASHES);
writer.write(boundary);
writer.write(TWO_DASHES);
writer.write(NEWLINE);
writer.flush();
}
@Override
public boolean retrySupported()
{
for (Part part : parts)
{
if (!part.content.retrySupported())
{
return false;
}
}
return true;
}
@Override
public MultipartFormContent setMediaType(HttpMediaType mediaType)
{
super.setMediaType(mediaType);
return this;
}
/**
* Returns an unmodifiable view of the parts of the HTTP multipart request.
*/
public final Collection<Part> getParts()
{
return Collections.unmodifiableCollection(parts);
}
/**
* Sets the parts of the HTTP multipart request.
* <p/>
* <p>
* Overriding is only supported for the purpose of calling the super implementation and changing
* the return type, but nothing else.
* </p>
*/
public MultipartFormContent setParts(Collection<Part> parts)
{
this.parts = new ArrayList<Part>(parts);
return this;
}
/**
* Adds an HTTP multipart part.
* <p/>
* <p>
* Overriding is only supported for the purpose of calling the super implementation and changing
* the return type, but nothing else.
* </p>
*/
public MultipartFormContent addPart(Part part)
{
parts.add(Preconditions.checkNotNull(part));
return this;
}
/**
* Returns the boundary string to use.
*/
public final String getBoundary()
{
return getMediaType().getParameter("boundary");
}
/**
* Sets the boundary string to use.
* <p/>
* <p>
* Defaults to {@code "__END_OF_PART__"}.
* </p>
* <p/>
* <p>
* Overriding is only supported for the purpose of calling the super implementation and changing
* the return type, but nothing else.
* </p>
*/
public MultipartFormContent setBoundary(String boundary)
{
getMediaType().setParameter("boundary", Preconditions.checkNotNull(boundary));
return this;
}
/**
* Single part of a multi-part request.
* <p/>
* <p>
* Implementation is not thread-safe.
* </p>
*/
public static final class Part
{
/**
* Name of this FormPart
*/
private String name;
/**
* FileName of the File being uploaded or {@code null} for none.
*/
private String filename;
/**
* HTTP header or {@code null} for none.
*/
private HttpHeaders headers;
/**
* HTTP content or {@code null} for none.
*/
private HttpContent content;
/**
* HTTP encoding or {@code null} for none.
*/
private HttpEncoding encoding;
public Part()
{
this(null, null);
}
/**
* @param headers HTTP headers or {@code null} for none
* @param content HTTP content or {@code null} for none
*/
public Part(String name, HttpContent content)
{
setName(name);
setContent(content);
}
/**
* Returns the HTTP content or {@code null} for none.
*/
public HttpContent getContent()
{
return content;
}
/**
* Sets the HTTP content or {@code null} for none.
*/
public Part setContent(HttpContent content)
{
this.content = content;
if (content instanceof FileContent)
{
final File file = ((FileContent) content).getFile();
if (file != null && file.exists())
{
setFilename(file.getName());
}
}
return this;
}
public String getName()
{
return name;
}
public Part setName(final String name)
{
this.name = name;
return this;
}
public String getFilename()
{
return filename;
}
/**
* Set the HTTP form-part filename or null for none.
*
* @param filename
*/
public Part setFilename(final String filename)
{
this.filename = filename;
return this;
}
/**
* Returns the HTTP headers or {@code null} for none.
*/
public HttpHeaders getHeaders()
{
return headers;
}
/**
* Sets the HTTP headers or {@code null} for none.
*/
public Part setHeaders(final HttpHeaders headers)
{
this.headers = headers;
return this;
}
/**
* Returns the HTTP encoding or {@code null} for none.
*/
public HttpEncoding getEncoding()
{
return encoding;
}
/**
* Sets the HTTP encoding or {@code null} for none.
*/
public Part setEncoding(HttpEncoding encoding)
{
this.encoding = encoding;
return this;
}
}
}
This is working for me, I am using against Cloudinary.