На самом деле ваш код может содержать небольшую ошибку, именно из-за того, как вы обрабатываете буферы.
Когда вы читаете буфер из исходного файла, метод read(byte[])
возвращает количество фактически прочитанных байтов. Нет никакой гарантии, что фактически все 8192 байта были прочитаны.
Предположим, у вас есть файл с 10000 байтами. Ваша первая операция чтения читает 8192 байта. Однако ваша вторая операция чтения будет считывать только 1808 байт. Третья операция вернет -1.
В первом чтении вы пишете именно те байты, которые прочитали, потому что читаете полный буфер. Но во втором чтении ваш буфер на самом деле содержит 1808 правильных байтов, а оставшиеся 6384 байта неверны - они все еще там с предыдущего чтения.
В этом случае вам повезло, потому что это происходит только в последнем буфере, который вы пишете. Таким образом, тот факт, что вы прекращаете чтение на стороне клиента, когда достигаете предварительно отправленной длины, заставляет вас пропускать те 6384 неправильных байта, которые вы не должны были отправлять в любом случае.
Но на самом деле нет никакой гарантии, что чтение из файла вернет 8192 байта, даже если конец еще не достигнут. Контракт метода не гарантирует этого, и это зависит от ОС и базовой файловой системы. Например, он может отправить вам 5000 байт при первом чтении и 5000 при втором чтении. В этом случае вы будете посылать 3192 неправильных байтов в середине файла.
Следовательно, ваш код должен выглядеть так:
byte[] filebuffer = new byte[8192];
int read = 0;
while(( read = fis.read(filebuffer)) > 0){
dos.write(filebuffer,0,read);
dos.flush();
}
очень похоже на код, который вы имеете на принимающей стороне. Это гарантирует, что будут записаны только фактические прочитанные байты.
Так что на самом деле нет ничего волшебного в том, как обрабатываются буферы. Вы даете потоку буфер, вы говорите ему, сколько буфера ему разрешено заполнять, но нет никакой гарантии, что он заполнит все это. Это может заполнить меньше, и у вас будет , чтобы заботиться и использовать только ту часть, которая, по его словам, заполняет.
Еще одна серьезная ошибка, которую вы совершаете, заключается в том, чтобы просто преобразовать полученный long
в int
в этой строке:
int remaining = (int)longsize;
Файлы могут быть длиннее, чем целое число. Особенно такие вещи, как длинные видео и т. Д. Вот почему вы получаете этот номер как long
. Не обрезай это так. Оставьте remaining
как long
и измените его на int
только после того, как вы взяли минимум (потому что вы знаете, что минимум всегда будет в диапазоне int
).
long remaining = longsize;
long fileBufferLen = filebuffer.length;
while((read = dis.read(filebuffer, 0, (int)Math.min(fileBufferLen, remaining))) > 0){
...
}
Кстати, нет реальной причины использовать для этого DataOutputStream
и DataInputStream
. read(byte[])
, read(byte[],int,int)
, write(byte[])
и write(byte[],int,int)
наследуются от базового InputStream
, и нет никаких оснований не использовать сокет OutputStream
/ InputStream
напрямую или использовать BufferedOutputStream
/ BufferedOutputStream
чтобы обернуть его. Также нет необходимости использовать flush
, пока вы не закончите писать / читать.
Кроме того, не забудьте, по крайней мере, закрыть потоки ввода / вывода файлов, когда вы закончите с ними. Возможно, вы захотите оставить потоки ввода / вывода сокетов открытыми для продолжения связи, но нет необходимости держать сами файлы открытыми, это может вызвать проблемы. Используйте попытку с ресурсами, чтобы гарантировать, что они закрыты.