Проблемы с загрузкой бинарных файлов на Android

У меня проблемы с загрузкой двоичного файла (видео) в моем приложении из Интернета. В Quicktime, если я загружаю его напрямую, он работает нормально, но через мое приложение он каким-то образом перепутался (хотя в текстовом редакторе они выглядят точно так же). Вот пример:

    URL u = new URL("http://www.path.to/a.mp4?video");
    HttpURLConnection c = (HttpURLConnection) u.openConnection();
    c.setRequestMethod("GET");
    c.setDoOutput(true);
    c.connect();
    FileOutputStream f = new FileOutputStream(new File(root,"Video.mp4"));


    InputStream in = c.getInputStream();

    byte[] buffer = new byte[1024];
    int len1 = 0;
    while ( (len1 = in.read(buffer)) > 0 ) {
         f.write(buffer);
    }
    f.close();

person Isaac Waller    schedule 23.02.2009    source источник


Ответы (6)


Я не знаю, единственная ли это проблема, но у вас есть классический сбой Java: вы не рассчитываете на тот факт, что read() всегда разрешено возвращать меньше байтов, чем вы просите. Таким образом, ваше чтение может получить менее 1024 байтов, но ваша запись всегда записывает ровно 1024 байта, возможно, включая байты из предыдущей итерации цикла.

Исправить с:

 while ( (len1 = in.read(buffer)) > 0 ) {
         f.write(buffer,0, len1);
 }

Возможно, более высокая задержка сети или меньшие размеры пакетов 3G на Android усугубляют этот эффект?

person Ry4an Brase    schedule 23.02.2009
comment
Какая глупая ошибка... спасибо! Вот что происходит, когда вы не читаете учебник должным образом :) - person Isaac Waller; 23.02.2009
comment
Как насчет инициализации буфера? Как насчет защиты от исключений? Как насчет высвобождения ресурсов? Я думаю, что это хороший, но не полный ответ. Здесь есть и другие более полные ответы. - person AlikElzin-kilaka; 23.06.2011
comment
У меня такая же проблема.. но в моем случае даже это решение не имеет значения. Я все еще получаю файл 46k для изображения 430k. - person lavelle; 15.07.2011
comment
Я хотел бы отметить, что тест › 0 может преждевременно закончить чтение. В документации сказано, что -1 возвращается в конце потока. - person Clint; 19.11.2011
comment
@Clint: правда, но в документации также говорится (начиная с java 5), ​​что 0 не может быть возвращено, если параметр 'len' не равен 0 (если байт недоступен (...) -1 возвращается; в противном случае, по крайней мере читается один байт). В Java 2 можно вернуть 0. - person njzk2; 27.02.2012
comment
Преобразование InputStream в BufferedInputStream, а FileOutputStream в BufferedOutputStream значительно облегчит вам жизнь. - person NickPro; 06.05.2014
comment
Буферизация оболочек, безусловно, хорошая идея для любого сетевого ввода-вывода, но они не решают проблему — им по-прежнему разрешено возвращать меньше байтов, чем запрошено. - person Ry4an Brase; 07.05.2014
comment
@Clint Итак, как именно тест › 0 может преждевременно закончить чтение? Быть ясным. - person user207421; 20.06.2017
comment
@ndk Zero может быть возвращен только в том случае, если буфер имеет нулевую длину или указан нулевой счетчик. Включая Java 1 и 2. - person user207421; 20.06.2017
comment
Ты знал, что ты Иисус? Вы просто спасли меня от ада :) - person codezombie; 24.12.2017

Одной из проблем является ваше чтение буфера. Если каждое чтение входного потока не является точным числом, кратным 1024, вы скопируете неверные данные. Использовать:

byte[] buffer = new byte[1024];
int len1 = 0;
while ( (len1 = in.read(buffer)) != -1 ) {
  f.write(buffer,0, len1);
}
person Clint    schedule 23.02.2009

Я исправил код на основе предыдущих отзывов в этой теме. Я тестировал с использованием eclipse и нескольких больших файлов. Он работает нормально. Просто скопируйте и вставьте это в свою среду и измените путь http и местоположение, в которое вы хотите загрузить файл.

try {
    //this is the file you want to download from the remote server
    String path ="http://localhost:8080/somefile.zip";
    //this is the name of the local file you will create
    String targetFileName
        boolean eof = false;
    URL u = new URL(path);
    HttpURLConnection c = (HttpURLConnection) u.openConnection();
    c.setRequestMethod("GET");
    c.setDoOutput(true);
    c.connect();
    FileOutputStream f = new FileOutputStream(new File("c:\\junk\\"+targetFileName));
        InputStream in = c.getInputStream();
        byte[] buffer = new byte[1024];
        int len1 = 0;
        while ( (len1 = in.read(buffer)) > 0 ) {
        f.write(buffer,0, len1);
                 }
    f.close();
    } catch (MalformedURLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } catch (ProtocolException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

Удачи Алиреза Агамохаммади

person grepit    schedule 20.04.2011
comment
Таким образом, будет загружен тот же файл. я имею в виду, если файл уже загружен, он выдаст предупреждение? - person Loshi; 15.08.2012

Просто используйте метод копирования Apache (Apache Commons IO) — преимущество использования Java!

IOUtils.copy(is, os);

Не забудьте закрыть потоки в блоке finally:

try{
      ...
} finally {
  IOUtils.closeQuietly(is);
  IOUtils.closeQuietly(os);
}
person AlikElzin-kilaka    schedule 20.04.2011
comment
И не пересекайте ручьи. - person Jarett Millard; 22.06.2012

person    schedule
comment
Мне тоже нравится однострочное решение. Однако перед записью в файл следует проверить объект, иначе файл будет создан, даже если возникнут проблемы с загрузкой. Поэтому в следующий раз вы можете попытаться открыть поврежденный файл. - person thanhbinh84; 28.05.2013
comment
Как мне динамически назвать загруженный файл так же, как исходное имя файла? - person Compaq LE2202x; 19.12.2013
comment
С помощью этого метода вам нужно будет добавить несколько вещей, например, получить заголовок Content-Disposition, содержащий имя файла. - person njzk2; 19.12.2013
comment
Потрясающе .. (+1) .. это работает надежно .. и загруженный PDF-файл никогда не повреждается .. - person Rat-a-tat-a-tat Ratatouille; 05.07.2014
comment
@doreamon - как мне проверить сущность ?? используя его метод contentLength? Пожалуйста помогите - person Rat-a-tat-a-tat Ratatouille; 05.07.2014
comment
@Rat-a-tat-a-tatRatatouille, просто проверьте, если (entity!= null) перед записью в файл. - person thanhbinh84; 08.07.2014
comment
@Rat-a-tat-a-tatRatatouille, doreamon, вам также нужно проверить код ответа в сущности, чтобы быть в безопасности. - person njzk2; 08.07.2014

person    schedule
comment
Этот ответ не сработает. Сетевые подключения в основном потоке будут вызывать android.os.NetworkOnMainThreadException. - person JBirdVegas; 09.11.2014
comment
@JBirdVegas Не запускайте сетевые операции в основном потоке. Пожалуйста, создайте рабочий поток. - person ; 31.05.2015
comment
Используйте AsyncTask doInBackground для выполнения кода try {}catch. и удалите из него setDoOutput(true). - person AndroidGeek; 05.08.2015