simple HttpURLConnection POST file multipart/form-data from android to google blobstore
I have very little idea how html works.What i want to do is exactly similar to the following but on android
<body>
<form action="<%= some_url %>" method="post" enctype="multipart/form-data">
<input type="file" name="myFile">
<input type="submit" value="Submit">
</form>
</body>
I tried the following code -
private static void postToUrl(String url_to_upload_on,
String file_name_with_ext, byte[] byteArray) {
String attachmentName = "file";
String attachmentFileName = file_name_with_ext;
String crlf = "\r\n";
String twoHyphens = "--";
String boundary = "*****";
try{
URL url = new URL(url_to_upload_on);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty(
"Content-Type", "multipart/form-data;boundary=" + boundary);
DataOutputStream request = new DataOutputStream(
connection.getOutputStream());
request.writeBytes(twoHyphens + boundary + crlf);
request.writeBytes("Content-Disposition: form-data; name=\"" +
attachmentName + "\";filename=\"" +
attachmentFileName + "\"" + crlf);
request.writeBytes(crlf);
request.write(byteArray);
request.writeBytes(crlf);
request.writeBytes(twoHyphens + boundary +
twoHyphens + crlf);
request.flush();
request.close();
}catch(Exception e){
e.printStackTrace();
}
}
this gives me no direct errors but when i get error-stream using-
Log.w(TAG, "connection.getErrorStream() = " + connection.getErrorStream());
i get this-
12-14 18:25:54.911: W/uploadToBlobStore(30558): httpUrlConnection.getErrorStream() = com.android.okhttp.internal.http.HttpTransport$FixedLengthInputStream@426dd5a8
with no success.
PS- I am uploading a file to google blobstore
PS- I can not use Apache http libraries or its multipart class as android says its depreciated
EDIT 1
Now I am using the following code but it is working only for files less then 2.3Mb -
private static void postToUrl3(String url_to_upload_on,
String file_name_with_ext, byte[] byteArray, String mimeType) {
CloseableHttpClient httpClient = null;
try {
httpClient = HttpClientBuilder.create().build();
HttpPost postRequest = new HttpPost(url_to_upload_on);
MultipartEntityBuilder reqEntity = MultipartEntityBuilder.create();
reqEntity.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
ByteArrayBody bab = new ByteArrayBody(byteArray, file_name_with_ext);
reqEntity.addPart("file", bab);
postRequest.setEntity(reqEntity.build());
httpClient.execute(postRequest);// takes time
} catch (Exception e) {
Log.w("uploadToBlobStore", "postToUrl Exception e = " + e);
e.printStackTrace();
} finally {
if (httpClient != null) {
Log.w("uploadToBlobStore", "connection.closing ");
try {
httpClient.close();
} catch (IOException e) {
Log.w("uploadToBlobStore", "connection.closing errot e = "
+ e);
e.printStackTrace();
}
}
}
}
how to make it work with larger files?
PS- i am sending it to blobstore and i did set the maxUploadSizeBytes
and MaxUploadSizeBytesPerBlob
to 30MB.I am not able to figure out the issue with size because google blobstore documentation says -
Google App Engine includes the Blobstore service, which allows applications to serve data objects limited only by the amount of data that can be uploaded or downloaded over a single HTTP connection.
So can it be a problem with http connection? and if so, how can i configure it.
Solution 1:
I am using HttpURLConnection
to achieve this.
Create one Multipart custom class ::
public class MultipartUtility {
private final String boundary;
private static final String LINE_FEED = "\r\n";
private HttpURLConnection httpConn;
private String charset;
private OutputStream outputStream;
private PrintWriter writer;
/**
* This constructor initializes a new HTTP POST request with content type
* is set to multipart/form-data
*
* @param requestURL
* @param charset
* @throws IOException
*/
public MultipartUtility(String requestURL, String charset)
throws IOException {
this.charset = charset;
// creates a unique boundary based on time stamp
boundary = "===" + System.currentTimeMillis() + "===";
URL url = new URL(requestURL);
httpConn = (HttpURLConnection) url.openConnection();
httpConn.setUseCaches(false);
httpConn.setDoOutput(true); // indicates POST method
httpConn.setDoInput(true);
httpConn.setRequestProperty("Content-Type",
"multipart/form-data; boundary=" + boundary);
outputStream = httpConn.getOutputStream();
writer = new PrintWriter(new OutputStreamWriter(outputStream, charset),
true);
}
/**
* Adds a form field to the request
*
* @param name field name
* @param value field value
*/
public void addFormField(String name, String value) {
writer.append("--" + boundary).append(LINE_FEED);
writer.append("Content-Disposition: form-data; name=\"" + name + "\"")
.append(LINE_FEED);
writer.append("Content-Type: text/plain; charset=" + charset).append(
LINE_FEED);
writer.append(LINE_FEED);
writer.append(value).append(LINE_FEED);
writer.flush();
}
/**
* Adds a upload file section to the request
*
* @param fieldName name attribute in <input type="file" name="..." />
* @param uploadFile a File to be uploaded
* @throws IOException
*/
public void addFilePart(String fieldName, File uploadFile)
throws IOException {
String fileName = uploadFile.getName();
writer.append("--" + boundary).append(LINE_FEED);
writer.append(
"Content-Disposition: form-data; name=\"" + fieldName
+ "\"; filename=\"" + fileName + "\"")
.append(LINE_FEED);
writer.append(
"Content-Type: "
+ URLConnection.guessContentTypeFromName(fileName))
.append(LINE_FEED);
writer.append("Content-Transfer-Encoding: binary").append(LINE_FEED);
writer.append(LINE_FEED);
writer.flush();
FileInputStream inputStream = new FileInputStream(uploadFile);
byte[] buffer = new byte[4096];
int bytesRead = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
inputStream.close();
writer.append(LINE_FEED);
writer.flush();
}
/**
* Adds a header field to the request.
*
* @param name - name of the header field
* @param value - value of the header field
*/
public void addHeaderField(String name, String value) {
writer.append(name + ": " + value).append(LINE_FEED);
writer.flush();
}
/**
* Completes the request and receives response from the server.
*
* @return a list of Strings as response in case the server returned
* status OK, otherwise an exception is thrown.
* @throws IOException
*/
public List<String> finish() throws IOException {
List<String> response = new ArrayList<String>();
writer.append(LINE_FEED).flush();
writer.append("--" + boundary + "--").append(LINE_FEED);
writer.close();
// checks server's status code first
int status = httpConn.getResponseCode();
if (status == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(
httpConn.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
response.add(line);
}
reader.close();
httpConn.disconnect();
} else {
throw new IOException("Server returned non-OK status: " + status);
}
return response;
}
}
Use it (async way) ::
MultipartUtility multipart = new MultipartUtility(requestURL, charset);
// In your case you are not adding form data so ignore this
/*This is to add parameter values */
for (int i = 0; i < myFormDataArray.size(); i++) {
multipart.addFormField(myFormDataArray.get(i).getParamName(),
myFormDataArray.get(i).getParamValue());
}
//add your file here.
/*This is to add file content*/
for (int i = 0; i < myFileArray.size(); i++) {
multipart.addFilePart(myFileArray.getParamName(),
new File(myFileArray.getFileName()));
}
List<String> response = multipart.finish();
Debug.e(TAG, "SERVER REPLIED:");
for (String line : response) {
Debug.e(TAG, "Upload Files Response:::" + line);
// get your server response here.
responseString = line;
}
Solution 2:
use okhttp and use following snippet (taken from recipes)
adjust the header values according to what your server expects.
private static final String IMGUR_CLIENT_ID = "...";
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
// Use the imgur image upload API as documented at https://api.imgur.com/endpoints/image
RequestBody requestBody = new MultipartBuilder()
.type(MultipartBuilder.FORM)
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"title\""),
RequestBody.create(null, "Square Logo"))
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"image\""),
RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
.build();
Request request = new Request.Builder()
.header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
.url("https://api.imgur.com/3/image")
.post(requestBody)
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
Solution 3:
As alternative you can use Retrofit.
You can specify a call like this:
@Multipart
@POST("/user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
then create it like this:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.build();
GitHubService service = retrofit.create(GitHubService.class);
and finally execute it like this:
service.updateUser(Photo, description).enqueue()
--> asynchronous
service.updateUser(Photo, description).execute()
--> synchronous
See the documentation here