Вопрос по java – Java многократная передача файла через сокет

21

Хорошо, попытка передать указанный каталог файлов через сокет, удалить объекты каталога из массива, чтобы остались только файлы, и передать их 1 на 1 через один сокет. Архиватор здесь заполнен ТОЛЬКО файлами, без каталогов. Вот код получения и отправки для клиента и сервера соответственно. Код работает без ошибок, за исключением того, что ВСЕ данные записываются в первый файл. Последующие файлы создаются в папке сервера, но они имеют размер 0 байт. Любой вклад будет принята с благодарностью.

ЭТО СЕРВЕРНЫЙ КОДЕКС ДЛЯ ПОЛУЧЕНИЯ ФАЙЛОВ

public void receive(){


    try {
        DataInputStream dis = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
        DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
//read the number of files from the client
        int number = dis.readInt();
        ArrayList<File>files = new ArrayList<File>(number);
        System.out.println("Number of Files to be received: " +number);
        //read file names, add files to arraylist
        for(int i = 0; i< number;i++){
            File file = new File(dis.readUTF());
            files.add(file);
        }
        int n = 0;
        byte[]buf = new byte[4092];

        //outer loop, executes one for each file
        for(int i = 0; i < files.size();i++){

            System.out.println("Receiving file: " + files.get(i).getName());
            //create a new fileoutputstream for each new file
            FileOutputStream fos = new FileOutputStream("C:\\users\\tom5\\desktop\\salestools\\" +files.get(i).getName());
            //read file
            while((n = dis.read(buf)) != -1){
                fos.write(buf,0,n);
                fos.flush();
            }
            fos.close();
        }

    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();

    }


}

ЭТО КОД КЛИЕНТА ДЛЯ ОТПРАВКИ ФАЙЛОВ

public void send(ArrayList<File>files){

    try {
        DataInputStream dis = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
        DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
        System.out.println(files.size());
//write the number of files to the server
        dos.writeInt(files.size());
        dos.flush();

        //write file names 
        for(int i = 0 ; i < files.size();i++){
            dos.writeUTF(files.get(i).getName());
            dos.flush();
        }

        //buffer for file writing, to declare inside or outside loop?
        int n = 0;
        byte[]buf = new byte[4092];
        //outer loop, executes one for each file
        for(int i =0; i < files.size(); i++){

            System.out.println(files.get(i).getName());
            //create new fileinputstream for each file
            FileInputStream fis = new FileInputStream(files.get(i));

            //write file to dos
            while((n =fis.read(buf)) != -1){
                dos.write(buf,0,n);
                dos.flush();

            }
            //should i close the dataoutputstream here and make a new one each time?
        }
        //or is this good?
        dos.close();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }


}
в основном да. ControlAltDel
другой вариант заключался бы в том, чтобы записать размеры каждого файла вместе с именами файлов в «заголовок». Тогда ваш серверный код может знать, когда прекратить запись в каждый файл и начать следующий. GreyBeardedGeek
Хм, все еще с той же проблемой, все данные записываются в первый файл, не знаю, что я делаю неправильно. Tom 5
По сути вы пытаетесь дублировать функциональность (S) FTP. Вы не пометили этот вопрос как домашнее задание, поэтому можете ли вы объяснить, почему вы пытаетесь это сделать, а не используете библиотеку FTP? ControlAltDel
Пытаясь научить себя Java, просто делаю несколько простых проектов, чтобы поиграть с io впервые. Я чувствую, что мой dos.write (buf, 0, n) и его партнер по приему просто продолжают читать и записывать, таким образом, все данные записываются в первый файл? Нужно ли добавить элемент управления для сигнализации, когда первый файл был полностью записан? Tom 5

Ваш Ответ

2   ответа
23

read() возвращает -1. Это условие окончания потока (EOS). EOS происходит, когда узел закрывает соединение. Не тогда, когда он заканчивает писать один файл.

Вам нужно отправить размер файла перед каждым файлом. Вы уже делаете то же самое с количеством файлов. Затем убедитесь, что вы прочитали именно столько байтов для этого файла:

String filename = dis.readUTF();
long fileSize = dis.readLong();
FileOutputStream fos = new FileOutputStream(filename);
while (fileSize > 0 && (n = dis.read(buf, 0, (int)Math.min(buf.length, fileSize))) != -1)
{
  fos.write(buf,0,n);
  fileSize -= n;
}
fos.close();

Вы можете заключить все это в цикл, который заканчивается, когдаreadUTF() бросаетEOFException. И наоборот, конечно, вы должны позвонитьwriteUTF(filename) а такжеwriteLong(filesize) у отправителя, перед отправкой данных.

Не могуshutdownOutput также используется для обозначенияEOF? Cratylus
@ user384706 Да. Один раз за соединение. Таким образом, вы можете отправить только один файл. user207421
Это сработало отлично, я запутался в части n = dis.read (buf, 0, Math.min), определяет ли math.min размер файла меньше размера буфера? и если это так, он будет только частично заполнять буфер только file.length? Tom 5
@ Tom TheMath.min() должен правильно обрабатывать последний буфер. Вы не можете предполагать, что размер файла кратен длине буфера. user207421
Спасибо @EJP ... это хорошая вещь для изучения DkPathak
1

send

byte[] done = new byte[3];
String str = "done";  //randomly anything
done = str.getBytes();
for(int i =0; i < files.size(); i++){
    System.out.println(files.get(i).getName());
    FileInputStream fis = new FileInputStream(files.get(i));
    while((n =fis.read(buf)) != -1){
        dos.write(buf,0,n);
        System.out.println(n);
        dos.flush();
    }
 //should i close the dataoutputstream here and make a new one each time?                 
    dos.write(done,0,3);
    dos.flush();
}
        //or is this good?
        dos.close();

recieve

for(int i = 0; i < files.size();i++){
    System.out.println("Receiving file: " + files.get(i).getName());
 //create a new fileoutputstream for each new file
fos = new FileOutputStream("C:\\users\\tom5\\desktop\\salestools\\" +files.get(i).getName());
//read file
while((n = dis.read(buf)) != -1 && n!=3 ){
        fos.write(buf,0,n);
        fos.flush();
        }
            fos.close();
        }
Хммм, сейчас я отправляю размер файла и сохраняю его в массиве int. Я добавил это на свой сервер while loop, while ((n = dis.read (buf))! = -1 && bytesRead <fileSize)); Tom 5
спасибо вашей проблеме, я узнал что-то новое, что вы просили закрытьDataOutputStream после каждой передачи файла он фактически закрывает основной поток, и поэтому сокет закрывается.Close()-Closes this output stream and releases any system resources associated with the stream. Javadocs. Nikhar
ok, я тоже это пробовал, но потеря / выигрыш несколько байтов. Просто проверьте последний файл, который был передан, он теряет несколько байтов? Nikhar
Например, получая 5 файлов, иногда он получит все байты в первых 3 итерациях, а 2 последних файла будут 0 байтов, иногда он получит байты в 4-й итерации. Был довольно случайным Tom 5
Хотя, прочитайте вышеупомянутый пост, где Math.min был добавлен в while ((n = dis.read (buf, 0, Math.min (n, FileSizes [i])). Это полностью исправило проблему, но забрало мою мозг в процессе Tom 5

Похожие вопросы