Search code examples
javaandroidhttp-posthttpurlconnectionbackblaze

Sending Bitmap to Server (B2) via POST not working OKhttpClient HttpURLConnection


I am using Backblaze and managed to upload files (images and videos) to my buckets in my iOS App (swift 5) but I am unable to do so in my java android app.

I receive the correct auth token, download url, upload url and API Url.

But when I try to post it to that url its not working and I receive the following error

W/OkHttpClient: A connection to https://googleads.g.doubleclick.net/ was leaked. Did you forget to close a response body?
D/yyyy enclosed: saveImgToStorage:     buffer(com.android.okhttp.internal.http.Http1xStream$FixedLengthSource@e5232ab).inputStream()
saveImgToStorage: 401
W/System.err: java.net.ProtocolException: cannot write request body after response has been read
    at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:264)
    at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getOutputStream(DelegatingHttpsURLConnection.java:218)
    at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getOutputStream(Unknown Source:0)
    at com.flax.de.Helper.StorageService.saveImgToStorage(StorageService.java:192)
    at com.flax.de.Helper.StorageService.getUploadUrl(StorageService.java:142)
    at com.flax.de.Helper.StorageService.getAuthData(StorageService.java:95)
W/System.err:     at com.flax.de.Categories.NewPostFragment$3.run(NewPostFragment.java:341)
    at java.lang.Thread.run(Thread.java:764)

The function trying to post the data to the upload url is the following

   static private void saveImgToStorage(Context context, String id, String uploadUrl, String uploadAuthorizationToken
        , String downloadUrl, Uri imageUrl, String bucketName, Interfaces.OnSuccessHashMap onSuccess
        , Interfaces.OnError onError) throws IOException {

    String photoUrl = downloadUrl+"/file/"+bucketName+"/"+id;
    HashMap hashMap = new HashMap();
    hashMap.put(API.databaseStrings.URL, photoUrl);
    Bitmap bmp = MediaStore.Images.Media.getBitmap(context.getContentResolver(), imageUrl);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    bmp.compress(Bitmap.CompressFormat.JPEG, 100, baos);
    byte[] fileData = baos.toByteArray();
    String contentType = "image/jpg";
    String sha1 = "do_not_verify";



    HttpURLConnection connection = null;
    try {
        URL url = new URL(uploadUrl);
        connection = (HttpURLConnection)url.openConnection();
        connection.setRequestMethod("POST");
        connection.setRequestProperty("Authorization", uploadAuthorizationToken);
        connection.setRequestProperty("Content-Type", contentType);
        connection.setRequestProperty("X-Bz-File-Name", id);
        connection.setRequestProperty("X-Bz-Content-Sha1", sha1);
        connection.setDoOutput(true);

        Log.d("yyyy enclosed", "saveImgToStorage: " + connection.getErrorStream().toString());
        Log.d("yyyy enclosed", "saveImgToStorage: " + connection.getResponseCode());

        DataOutputStream writer = new DataOutputStream(connection.getOutputStream());
        writer.write(fileData);
        String jsonResponse = myInputStreamReader(connection.getInputStream());
        JSONObject obj = new JSONObject(jsonResponse);


        System.out.println(jsonResponse);
    } catch (Exception e) {
        e.printStackTrace();
        onError.onError();
    } finally {
        connection.disconnect();
    }
}

Error code is as you see

401

I have no idea why its not working. As I said it is working perfectly fine on my iOS devices which uses basically the same code only written in swift (taken from the guide from back blaze) I personally think its not an issue from back blaze but rather that my request is wrong? Or the format of my bitmap?

Or maybe I have to do it asynchronously? I tried it with a thread before move it out from the main thread but it did not work either.

W/System.err: java.io.FileNotFoundException: https://pod-000-1070-11.backblaze.com/b2api/v2/b2_upload_file/afec4cd53b2f323f715a0c18/c000_v0001070_t0002
    at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:251)
    at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getInputStream(DelegatingHttpsURLConnection.java:210)
    at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getInputStream(Unknown Source:0)

is what I receive if use this code

      String photoUrl = downloadUrl+"/file/"+bucketName+"/"+id;
    HashMap hashMap = new HashMap();
    hashMap.put(API.databaseStrings.URL, photoUrl);
    Bitmap bmp = MediaStore.Images.Media.getBitmap(context.getContentResolver(), imageUrl);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    bmp.compress(Bitmap.CompressFormat.JPEG, 100, baos);
    byte[] fileData = baos.toByteArray();
    String contentType = "image/jpg";
    String sha1 = "do_not_verify";

    HttpURLConnection connection = null;
    String json = null;


    try {
        URL url = new URL(uploadUrl);
        connection = (HttpURLConnection)url.openConnection();
        connection.setRequestMethod("POST");
        connection.setRequestProperty("Authorization", uploadAuthorizationToken);
        connection.setRequestProperty("Content-Type", contentType);
        connection.setRequestProperty("X-Bz-File-Name", id);
        connection.setRequestProperty("X-Bz-Content-Sha1", sha1);
        connection.setDoOutput(true);
        DataOutputStream writer = new DataOutputStream(connection.getOutputStream());
        writer.write(fileData);
        String jsonResponse = myInputStreamReader(connection.getInputStream());
        System.out.println(jsonResponse);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        connection.disconnect();
    }

Solution

  • The issue in the first version of the function is these two lines:

    Log.d("yyyy enclosed", "saveImgToStorage: " + connection.getErrorStream().toString());
    Log.d("yyyy enclosed", "saveImgToStorage: " + connection.getResponseCode());
    

    Both getErrorStream() and getResponseCode() cause the request to be sent, and the response to be read, since you've asked for the error response and response code from the server. connection.getOutputStream() throws an error because it's too late to send the request body.

    You should remove these lines, or move them after the line:

    writer.write(fileData);
    

    HttpURLConnection is 'broken by design' in the way that it implicitly sends the request when you try to read the response.