Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Downloading corrupted files with OkHttp

The method I wrote to download files always produce corrupted files.

public static String okDownloadToFileSync(final String link, final String fileName, final boolean temp, DownloadStatusManager statusManager, ErrorDisplayerInterface errorDisplayerInterface) {

    Request request = new Request.Builder()
            .url(link)
            .build();


    OkHttpClient client = Api.getInstance().getOkHttpClient();
    OutputStream output = null;
    InputStream input = null;

    try {

        Response response = client.newCall(request).execute();

        //Add the file length to the statusManager
        final int contentLength = Integer.parseInt(response.header("Content-Length"));
        if (statusManager != null) {
            statusManager.add(Hash.md5(link), contentLength);
        }

        //Get content type to know extension
        final String contentType = response.header("Content-Type");
        final String ext = contentTypeMap.get(contentType);

        Log.i(TAG, link + "\n --> contentType = " + contentType + "\n --> ext = " + ext);

        if (ext == null) {
            Log.e(TAG, "-----------\next is null, seems like there is a problem with that url : \n         " + link + "\n----------");
            return null;
        } else if (ext.equals("json")) {
            Log.e(TAG, "-----------\ndownloadable file seems to be a json, seems like there is a problem with that url : \n         " + link + "\n----------");
            return null;
        }

        //Check if file already exists
        if (!temp && fileName != null) {
            File test = new File(M360Application.getContext().getFilesDir(), fileName + "." + ext);
            if (test.exists()) {
                Log.i(TAG, "File exists ! : " + test.getPath());
                test.delete();
                //return test.getAbsolutePath();
            }
        }

        // expect HTTP 200 OK, so we don't mistakenly save error report instead of the file
        if (!response.isSuccessful()) {
            errorDisplayerInterface.popWarn(null, "Error while downloading " + link, "connection.getResponseCode() != HttpURLConnection.HTTP_OK");
            return null;
        }

        input = response.body().byteStream();

        File file;
        if (temp) {
            file = File.createTempFile(UUID.randomUUID().toString(), ext, M360Application.getContext().getCacheDir());
        } else {
            file = new File(M360Application.getContext().getFilesDir(), fileName + "." + ext);
        }


        output = new FileOutputStream(file);
        
        output.write(response.body().bytes());

//            byte data[] = new byte[4096];
//            long total = 0;
//            int count;
//            while ((count = input.read(data)) != -1) {
//                output.write(data, 0, count);
//                total++;
//
//                if (statusManager != null) {
//                    statusManager.update(Hash.md5(link), contentLength - total);
//                }
//           }

        return file.getAbsolutePath();
    } catch (IOException e) {
        e.printStackTrace();
        errorDisplayerInterface.popError(null, e);

    } finally {
        if (statusManager != null) {
            statusManager.finish(Hash.md5(link));
        }
        try {
            if (output != null)
                output.close();
            if (input != null)
                input.close();
        } catch (IOException ignored) {
            ignored.printStackTrace();
        }

    }
    return null;
}

I access these file via adb, transfer them to my sccard, and there I see that they seem to have the proper size, but has no type according to for instance Linux file command.

Would you know what is missing and how to fix it?

Thank you.


Edit

Simpler version of the code ( but the bug is the same )

public static String okioDownloadToFileSync(final String link, final String fileName) throws IOException {

    Request request = new Request.Builder()
            .url(link)
            .build();


    OkHttpClient client = Api.getInstance().getOkHttpClient();
    Response response = client.newCall(request).execute();

    final int contentLength = Integer.parseInt(response.header("Content-Length"));

    //Get content type to know extension
    final String contentType = response.header("Content-Type");
    final String ext = contentTypeMap.get(contentType);

    // expect HTTP 200 OK, so we don't mistakenly save error report instead of the file
    if (!response.isSuccessful()) {
        return null;
    }

    File file = new File(M360Application.getContext().getFilesDir(), fileName + "." + ext);

    BufferedSink sink = Okio.buffer(Okio.sink(file));
    sink.writeAll(response.body().source());
    sink.close();

    Log.i(TAG, "file.length : " + file.length() + " | contentLength : " + contentLength);

    return file.getAbsolutePath();

}

The log : file.length : 2485394 | contentLength : 1399242


Solution

The problem was that I was getting the OkHttpClient from my API singleton, which was used by retrofit and had multiples interceptors. Those interceptors were polluting the response.

So I OkHttpClient client = Api.getInstance().getOkHttpClient(); became OkHttpClient client = new OkHttpClient.Builder().build(); and everything is now ok !

Thanks a lot. I'm dividing the method into smaller pieces right now.

like image 877
Renaud Favier Avatar asked Nov 08 '22 03:11

Renaud Favier


1 Answers

Instead of output.write(response.body().bytes()); try something like this

byte[] buff = new byte[1024 * 4];

while (true) {
   int byteCount = input.read(buff);
   if (byteCount == -1) {
       break;
   }
   output.write(buff, 0, byteCount);
}
like image 142
Chupik Avatar answered Nov 15 '22 06:11

Chupik