Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Передача файла изображения от сервера клиенту.  (Прочитано 21848 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Man1BLR
Участник

by
Offline Offline
Пол: Мужской

« : 09-11-2010 17:49 » 

Всем добрый день. Возникла такая ситуация: нужно от сервера передать файл изображения клиенту. Непродолжительный поиск по всемирной паутине результатов не дал, и я решил написать свой класс, который мог бы справиться с поставленной задачей. В итоге получилось следующее:
Код:
/**
 * Класс, который задумывался для передачи изображения, но походу им можно
 * передавать любые файлы, желательно небольшие
 * @author Murashko Arthur
 */
public class ImageFileTransfer {
    private static int bufferSize = 1024;

    /**
     * Данный метод передает файл клиенту, для того, чтобы принять этот файл
     * нужно вызвать метод ImageFileTransfer.read(...)
     * @param socket сокет клиента
     * @param imageFile файл, который нужно передать
     */
    public static void send(Socket socket, File imageFile) {
        FileInputStream inputFile = null;
        ObjectOutputStream out = null;
        ImagePackage imagePackage = null;
        byte [] bytes = new byte[bufferSize];
        int length = 0;

        try {
            try {
                inputFile = new FileInputStream(imageFile);
                out = new ObjectOutputStream(socket.getOutputStream());

                while (true) {
                    length = inputFile.read(bytes, 0, bufferSize);
                    if (length == -1) {
                        imagePackage = new ImagePackage(bytes, 0, true);
                        out.writeObject(imagePackage);
                        break;
                    }
                    imagePackage = new ImagePackage(bytes, length, false);
                    out.writeObject(imagePackage);
                    out.flush();
                }
            } finally {
                inputFile.close();
                System.out.println("Файл передан.");
            }
        } catch (FileNotFoundException ex) {
            Logger.getLogger(ImageFileTransfer.class.getName()).log(
                    Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(ImageFileTransfer.class.getName()).log(
                    Level.SEVERE, null, ex);
        }
    }

    /**
     * Данный метод сохраняет файл, посланный сервером в нужном месте
     * @param socket сокет клиента
     * @param name полное имя, с которым хотим сохранить файл
     * @return объект класса File ассоциированный с переданным файлом
     */
    public static File read(Socket socket, String name) {
        File imageFile = null;
        FileOutputStream outImgFile = null;
        ObjectInputStream input = null;
        ImagePackage imagePackage = null;
        try {
            try {
                imageFile = new File(name);
                outImgFile = new FileOutputStream(imageFile);
                input = new ObjectInputStream(socket.getInputStream());
                imagePackage = (ImagePackage) input.readObject();

                while (!imagePackage.isFinish()) {
                    outImgFile.write(imagePackage.getBytes(), 0,
                            imagePackage.getLength());
                    imagePackage = (ImagePackage) input.readObject();
                }
            } finally {
                outImgFile.close();
            }
        } catch (IOException ex) {
            Logger.getLogger(ImageFileTransfer.class.getName()).log(
                    Level.SEVERE, null, ex);
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(ImageFileTransfer.class.getName()).log(
                    Level.SEVERE, null, ex);
        }
        return imageFile;
    }

    private static class ImagePackage implements Serializable {
        private boolean finish;
        private int length;
        private byte[] bytes;

        public ImagePackage(byte[] b, int length, boolean f) {
            this.length = length;
            bytes = b.clone();
            finish = f;
        }

        public boolean isFinish() {
            return finish;
        }

        public byte[] getBytes() {
            return bytes;
        }

        public int getLength() {
            return this.length;
        }
    }
}
Как видно из данного кода, у меня передаются объекты. Вот данный нюанс и мучает меня больше всего. Изначально я просто методами read и write соотвественно классов InputStream и OutputStream передавал байты моего файла, причем порциями по 1024 байта, но я не мог отследить на клиенте, когда приходит последняя порция данных - у меня просто метод read блокировал поток и ждал следующих данных.
Так вот после всего, хотелось бы узнать у Вас, есть ли какие-то встроенные средства позволяющие передавать файлы через сокет в Java, а также более красивое решение данной проблемы. И, пожалуйста, не пишите об избыточности кода, сам знаю что и где.

* Client.java (0.88 Кб - загружено 1408 раз.)
* Server.java (1.13 Кб - загружено 1332 раз.)
* ImageFileTransfer.java (3.34 Кб - загружено 1345 раз.)
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 09-11-2010 20:03 » 

Man1BLR, ну ты ведь используешь разные Stream для чтения и записи - будь то файлы или сокеты. И у встроенных Stream такой проблемы нет (судя по твоему коду) - т.е. они знают либо размер потока, либо получают признак конца потока. Почему бы этим не воспользоваться в своей обёртке?
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Man1BLR
Участник

by
Offline Offline
Пол: Мужской

« Ответ #2 : 10-11-2010 05:34 » 

Dimka, тот код, который я написал, работает исправно. Просто изначально я делал так:
Код, который отправляет файл
Код:
File file = new File("имя файла");

inputFile = new FileInputStream(file);
int length = inputFile.read(bytes, 0, BUFFER_SIZE);

while (length != -1)
{
output.write(bytes, 0, length);
ch = inputFile.read(bytes, 0, BUFFER_SIZE);
}
где, output - это объект OutputStream, который возвращает socket.getOutputStream(), а bytes - массив размера BUFFER_SIZE
Код, который принимает файл
Код:
File file = new File("имя файла");
outputFile = new FileOutputStream(file);

length = input.read(bytes);
while (length != -1) {
output.write(bytes, 0, length);
length = input.read(bytes);
}
где input - это объект InputStream, который возвращает socket.getInputStream()
Все это происходило без какой-либо обертки. В результате, получалось так, что метод read() объекта InputStream - input, блокировал рабочий поток, ожидая получения новых данных, т.е. я не знал как послать признак конца файла, чтобы выйти из цикла, скажем так. В результате сделал обертку, где можно узнать, последний это объект или нет. Так вот хотелось бы узнать есть ли более изящное решение данного вопроса.
Записан
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #3 : 10-11-2010 07:37 » 

обычно используют 2 подхода:
1. в первых 4-х или 8-ми байтах передают размер следующих за ними данных
2. или использую признак конца данных (или спец последовательность или просто рвут коннект после передачи)
мне больше нравится первый подход
ну и чтение надо подкрутить, что-бы оно вычитывало только необходимый размер.
Записан

Странно всё это....
Man1BLR
Участник

by
Offline Offline
Пол: Мужской

« Ответ #4 : 10-11-2010 08:14 » 

Антон (LogRus), спасибо за идею... Как говорится все гениальное просто.
А я, короче, только что около часа думал как передать признак конца файла. Кстати, пришел к выводу, порывшись в исходных файла (например, SocketOutputStream), что его нереально передать. Буду очень рад, если кто-нибудь скажет, что это не так.
« Последнее редактирование: 10-11-2010 10:11 от Man1BLR » Записан
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #5 : 10-11-2010 10:50 » 

это не так Улыбаюсь
можно делать перекодировку бинарных данных в uuencode или base64 и у тебя появляется возможность использовать высвободившиеся значения для служебных целей Улыбаюсь
Записан

Странно всё это....
Man1BLR
Участник

by
Offline Offline
Пол: Мужской

« Ответ #6 : 11-11-2010 18:15 » 

Что ж, решив задачу двумя методами
  • передавая объекты
  • передавая байты
могу сказать следующее:
  • разница в трафике - метод 2 передал на 2.5 МБ меньше
  • разница во времени - метод 2 намного быстрее
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines