simple HttpURLConnection POST file multipart/form-data from android to google blobstore
Asked Answered
K

3

48

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 maxUploadSizeBytesand 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.

Kobold answered 14/12, 2015 at 20:57 Comment(2)
I was able to upload a file using multiPartEntity, check out the code I posted in the answer here: #34223480 Tick green if it works! cheers!Schoolboy
The answers to this question might help: #3325217Chur
M
9

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());
}
Millur answered 17/12, 2015 at 0:52 Comment(7)
sorry for late reply.Do i have to add this 'okHttp' as an external library?Kobold
ok i added okHttp using jar. but eclipse gives me error "The type okio.ByteString cannot be resolved. It is indirectly referenced from required .class files" at 'RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))'Kobold
@FlyingMonkey I suggest you use Android Studio.. if you absolutely need to use eclipse however, download the okio jar (github.com/square/okio) and add it the same way you added okhttpMillur
yes, don't use Eclipse, it's been discontinued by Android SDK. You don't need to add any jars manually in Android Studio, just write a line in your gradle file. Use okhttp with retrofit to make the calls much more simple. square.github.io/retrofit square.github.io/okhttpVermiculite
THANKYOU! This code is not the best solution, but the pointer to okhttp is! I just want to emphasise to anyone battling this problem of uploading a file from Android that okhttp (currently okhttp3) is the way to go! When I started on this and saw okhttp mentioned I was wary of it, because one is always reluctant to use a free library you've never heard of. However, having been forced to this solution, I can't recommend okhttp too highly. It looks to have a strong maintenance record, is easy to use and install (into AndroidStudio), and solves a problem which other methods didn't (for me).Constitutionalism
For current and neater solutions with okhttp as of Dec 2016, see #23513047Constitutionalism
@StephenHosking internally android has been using okhttp since 4.4 that's why i recommend using itMillur
M
5

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

Marlite answered 23/12, 2015 at 15:9 Comment(0)
R
-2

Volley is a good http library for multipart data. AndroidMultiPartEntity class is for progress listener.

    public class AndroidMultiPartEntity extends MultipartEntity

    {

    private final ProgressListener listener;

    public AndroidMultiPartEntity(final ProgressListener listener) {
    super();
    this.listener = listener;
    }

    public AndroidMultiPartEntity(final HttpMultipartMode mode, final ProgressListener listener) {
    super(mode);
    this.listener = listener;
    }

    public AndroidMultiPartEntity(HttpMultipartMode mode, final String boundary,
    final Charset charset, final ProgressListener listener) {
    super(mode, boundary, charset);
    this.listener = listener;
    }

    @Override
    public void writeTo(final OutputStream outstream) throws IOException {
    super.writeTo(new CountingOutputStream(outstream, this.listener));
    }

    public static interface ProgressListener {
    void transferred(long num);
    }

    public static class CountingOutputStream extends FilterOutputStream {

    private final ProgressListener listener;
    private long transferred;

    public CountingOutputStream(final OutputStream out,
                                final ProgressListener listener) {
        super(out);
        this.listener = listener;
        this.transferred = 0;
    }

    public void write(byte[] b, int off, int len) throws IOException {
        out.write(b, off, len);
        this.transferred += len;
        this.listener.transferred(this.transferred);
    }

    public void write(int b) throws IOException {
        out.write(b);
        this.transferred++;
        this.listener.transferred(this.transferred);
    }
    }
    }

    Call the Async task like this
    new UploadFileToServer().execute();


    The Call method:
    private class UploadFileToServer extends AsyncTask<Void, Integer, String> {
    @Override
    protected void onPreExecute() {

        super.onPreExecute();
    }

    @Override
    protected void onProgressUpdate(Integer... progress) {

    }

    @Override
    protected String doInBackground(Void... params) {

        return uploadFile();

    }

    private String uploadFile() {

        String responseString = null;

        HttpClient httpclient = new DefaultHttpClient();
        HttpPost httppost = new HttpPost(Config.Seeker_Image_Upload);

        try {
            AndroidMultiPartEntity entity = new AndroidMultiPartEntity(new AndroidMultiPartEntity.ProgressListener() {

                @Override
                public void transferred(long num) {
                    publishProgress((int) ((num / (float) totalSize) * 100));
                }
            });

            File sourceFile = new File(Path);

            // Adding file data to http body
            entity.addPart("logo", new FileBody(sourceFile));

            // Extra parameters if you want to pass to server
            //entity.addPart("website", new StringBody("www.androidhive.info"));

            // String emailaddress = UserActivity.emailaddress;

            /*preferences = SeekerProfile.this.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
            email_address = preferences.getString("EMAILADDRESS", "");*/
            entity.addPart("EMAILADDRESS", new StringBody(email_address));
            entity.addPart("OPER", new StringBody(Operation_recruiter_logo_upload));

            totalSize = entity.getContentLength();
            httppost.setEntity(entity);

            // Making server call
            HttpResponse response = httpclient.execute(httppost);
            HttpEntity r_entity = response.getEntity();

            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                // Server response
                responseString = EntityUtils.toString(r_entity);
            } else {
                responseString = "Error occurred! Http Status Code: " + statusCode;
            }

        } catch (ClientProtocolException e) {
            responseString = e.toString();
        } catch (IOException e) {
            responseString = e.toString();
        }

        return responseString;

        }

        @Override
        protected void onPostExecute(String result) {
        //Log.e(TAG, "Response from server: " + result);
        enter code here
        }

    }
Riobard answered 14/12, 2015 at 20:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.