2012-06-13 4 views
0

Я сделал небольшую программу для загрузки данных и записи их в файл.Неполный файл с использованием RandomAccessFile в java

Вот код:

public void run() 
{ 

    byte[] bytes = new byte[1024]; 
    int bytes_read; 

    URLConnection urlc = null; 
    RandomAccessFile raf = null; 
    InputStream i = null; 


    try 
    { 
     raf = new RandomAccessFile("file1", "rw"); 
    } 
    catch(Exception e) 
    { 
     e.printStackTrace(); 
     return; 
    } 

    try 
    { 
     urlc = new URL(link).openConnection(); 
     i = urlc.getInputStream(); 
    } 
    catch(Exception e) 
    { 
     e.printStackTrace(); 
     return; 
    } 

    while(canDownload()) 
    { 
     try 
     { 
      bytes_read = i.read(bytes); 
     } 
     catch(Exception e) 
     { 
      e.printStackTrace(); 
      return; 
     } 

     if(bytes_read != -1) 
     { 
      try 
      { 
       raf.write(bytes, 0, bytes_read); 
      } 
      catch(Exception e) 
      { 
       e.printStackTrace(); 
       return; 
      } 
     } 
     else 
     { 
      try 
      { 
       i.close(); 
       raf.close(); 
       return; 
      } 
      catch(Exception e) 
      { 
       e.printStackTrace(); 
       return; 
      } 
     } 
    } 
} 

Проблема заключается в том, что когда я загрузить большие файлы, я получаю несколько байтов, отсутствующих в конце файла. Я попытался изменить размер байтового массива на 2K, и проблема была решена. Но когда я загрузил более крупный файл (500 МБ), я снова потерял несколько байтов. Я сказал: «Хорошо, давайте попробуем с размером 4K». И я изменил размер байтового массива на 4K. Это сработало! Приятно, но потом я загрузил файл размером 4 ГБ, я снова потерял байты! Я сказал: «Прохладный, давайте попробуем с размером 8К». И затем я изменил размер байтового массива на 8K. Работал.

Мой первый вопрос: почему это происходит? (когда я изменяю размер буфера, файл не повреждается).

Хорошо, теоретически проблема с поврежденным файлом может быть решена путем изменения размера массива байтов на большие значения. Но есть еще одна проблема: как измерить скорость загрузки (за один интервал) с большими размерами байтов?

Например: предположим, что скорость загрузки составляет 2 КБ/с. И размер массива байтов составляет 4 К. Мой второй вопрос: как измерить скорость (за один интервал), если потоку придется ждать, пока массив байтов будет заполнен? Мой ответ должен быть: изменить размер массива байтов на меньшее значение. Но файл будет поврежден xD.

После попытки решить проблему самостоятельно, я потратил 2 дня на поиск через Интернет для решения. И ничего.

Прошу вас, ребята, ответить на мои два вопроса? Благодаря = D

Редактировать

Код для canDownload():

synchronized private boolean canDownload() 
{ 
    return can_download; 
} 
+2

Показать код для 'canDownload()' –

+0

Я добавил к тексту вопроса. – Yuri

+0

Предположим, что ваш размер файла 300 * 2k + 35 байт, вы пропускаете последние 35 байтов? – user1452132

ответ

2

Мой совет использовать проверенную библиотеку, такие как Apache Commons IO вместо того, чтобы пытаться свернуть свой собственный код. Для вашей конкретной проблемы взгляните на метод copyURLToFile(URL, File).

+0

Я собираюсь добавить функциональность в свою программу (пауза, резюме и т. Д.). И мне нужно измерить скорость загрузки. Это прототип менеджера загрузки. – Yuri

1

я бы:

  1. Изменение RandomAccessFile к FileOutputStream.

  2. Избавьтесь от canDownload(), вне зависимости от того, для чего он предназначен, и установите время ожидания чтения для соединения.

  3. Упрощение цикла копирования для этого:

    в то время как ((bytes_read = i.read (байт))> 0) { out.write (байт, 0, bytes_read); } out.close(); i.close();

с обработкой всех исключение вне этого цикла.

+0

Я хочу добавить функциональность в свою программу (например, приостанавливать и возобновлять загрузку). И для этого мне нужен RandomAccessFile. canDownload() теперь бесполезен, но вскоре он будет использоваться для управления пользовательскими командами для загрузки. В чем проблема с обработкой исключений внутри цикла? – Yuri

1

Я думаю, вы обнаружите, что проблема заключается в том, что вы закрыли базовый InputStream, в то время как RandomAccessFile все еще имел данные в своих буферах записи. Вот почему вы иногда пропускаете последние несколько байтов данных.

Состояние гонки между JVM, промывающим окончательную запись, и ваш вызов i.close().

Извлечение i.close() должно устранить проблему; это не обязательно, так как raf.close() все равно закрывает базовый поток, но таким образом вы даете RAF возможность сбросить все выдающиеся буферы, прежде чем он это сделает.

+0

Но если я удалю 'i.close()', соединение не будет закрыто, и я хочу закрыть это соединение после завершения загрузки. Я не понял связи между raf.write() и i.close(). Для меня важно размер байтового буфера (пожалуйста, посмотрите на мои два вопроса). – Yuri

+0

Как я уже говорил, raf.close() закрывает базовый поток. Размер вашего буфера совершенно не имеет отношения к вашей проблеме. Важно то, что вы неправильно очищаете буферы java.io перед закрытием потока/выхода из vm. Очевидным виновником является постороннее i.close(). Что касается измерения скорости, у вас есть суммарное количество байтов, и System.getCurrentTimeMillis() сообщит вам, сколько времени вы загружаете. Конечно, @ Anthony-accioly прав, если бы вы использовали org.apache.commons.io.input.CountingInputStream, вы бы уже закончили. – Recurse

+0

@Recurse Я не думаю, что вы правы. Между «InputStream i» и «RandomAccessFile raf» нет никакой связи; где вы это видите? 'i.close()' не является посторонним вообще. Кроме того, RandomAccessFile не имеет буфера, поэтому при вызове 'raf.write()' ничего не нужно скрывать. – user845279

Смежные вопросы