Загруженные файлы повреждены, если длина буфера> 1 - PullRequest
0 голосов
/ 28 декабря 2018

Я пытаюсь написать функцию, которая загружает файл по определенному URL.Функция создает поврежденный файл, если я не сделаю буфер массивом размером 1 (как в коде ниже).

Тернарный оператор над инициализацией буфера (который я планирую использовать) вместе с жестко запрограммированными целочисленными значениями, отличными от 1, создаст поврежденный файл.

Примечание: MAX_BUFFER_SIZE - это определенная константакак 8192 (2 ^ 13) в моем коде.

public static void downloadFile(String webPath, String localDir, String fileName) {
    try {
        File localFile;
        FileOutputStream writableLocalFile;
        InputStream stream;

        url = new URL(webPath);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        int size = connection.getContentLength(); //File size in bytes
        int read = 0; //Bytes read

        localFile = new File(localDir);

        //Ensure that directory exists, otherwise create it.
        if (!localFile.exists())
            localFile.mkdirs();

        //Ensure that file exists, otherwise create it.
        //Note that if we define the file path as we do below initially and call mkdirs() it will create a folder with the file name (I.e. test.exe). There may be a better alternative, revisit later.
        localFile = new File(localDir + fileName);
        if (!localFile.exists())
            localFile.createNewFile();

        writableLocalFile = new FileOutputStream(localFile);
        stream = connection.getInputStream();

        byte[] buffer;
        int remaining;
        while (read != size) {
            remaining = size - read; //Bytes still to be read
            //remaining > MAX_BUFFER_SIZE ? MAX_BUFFER_SIZE : remaining
            buffer = new byte[1]; //Adjust buffer size according to remaining data (to be read).

            read += stream.read(buffer); //Read buffer-size amount of bytes from the stream.
            writableLocalFile.write(buffer, 0, buffer.length); //Args: Bytes to read, offset, number of bytes
        }

        System.out.println("Read " + read + " bytes.");

        writableLocalFile.close();
        stream.close();
    } catch (Throwable t) {
        t.printStackTrace();
    }
}

Причина, по которой я написал это таким образом, заключается в том, что я могу предоставить пользователю индикатор выполнения в реальном времени во время загрузки.Я удалил его из кода, чтобы уменьшить беспорядок.

Ответы [ 2 ]

0 голосов
/ 28 декабря 2018

InputStream.read () может прочитать число байтов меньше, чем вы запрашивали.Но вы всегда добавляете в файл целый буфер.Вам необходимо записать фактическое число прочитанных байтов и добавить в файл только эти байты.

Дополнительно:

  1. Наблюдайте, как InputStream.read() возвращает -1 (EOF)
  2. Сервер может вернуть неправильный размер.Таким образом, проверка read != size опасна.Я бы советовал вообще не полагаться на HTTP-поле Content-Length.Вместо этого просто продолжайте чтение из входного потока, пока не нажмете EOF.
0 голосов
/ 28 декабря 2018
len = stream.read(buffer);
read += len;
writableLocalFile.write(buffer, 0, len); 

Вы не должны использовать buffer.length в качестве прочитанных байтов, вам необходимо использовать возвращаемое значение вызова read.Потому что он может вернуть короткое чтение, и тогда ваш буфер содержит мусор (0 байтов или данные из предыдущих чтений) после прочитанных байтов.

И кроме вычисления оставшихся и использования динамических буферов, просто перейдите на 16k или что-то подобное,Последнее чтение будет коротким, и это нормально.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...