Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Апплеты > upload


Автор: Gugle 18.3.2008, 17:30
Доброго времени суток!

Подскажите как реализовать передачу файла на сервер через Applet?
Нашел пакет fileupload для servlet. Но как сформировать метод post в apllet не понимаю.
Может кто подскажет как реализовать эту форму в applet-е?
Код

<form enctype="multipart/form-data" action="/editor/upload.do" method='post'>
    <input type='hidden' name='action' value='ploadfile'/>
        <table border='0' cellspacing="0" cellpadding="0">
            <tr>
                <td>Выбрать файл:</td>
                <td><input class='inp' name="filename" type="file"/></td>
                <td><input class='button' type="submit" value="Загрузить"/></td>
             </tr>
         </table>
 </form>

Автор: LSD 18.3.2008, 17:38
Для того чтобы апплет мог работать с файлами его придется подписать. Плюс там есть заморочки с установленностью JVM. Оно тебе реально надо грузить файлы апплетом?

Автор: Gugle 18.3.2008, 17:44
Подписать не проблема.

Цитата

Плюс там есть заморочки с установленностью JVM.


Это про что?

Цитата

Оно тебе реально надо грузить файлы апплетом?


Хотелось бы. Альтернатива типа перехода на страницу загрузки (как например кот я выложил) возможна, но как на jsp написать красивый загрузчик я незнаю, т.к. не знаю jsp.

Автор: LSD 18.3.2008, 17:53
Цитата(Gugle @  18.3.2008,  17:44 Найти цитируемый пост)
Это про что?

Про то что у клиента может быть не установлена JRE, или установленна старая версия.


Цитата(Gugle @  18.3.2008,  17:44 Найти цитируемый пост)
Хотелось бы. Альтернатива типа перехода на страницу загрузки (как например кот я выложил) возможна, но как на jsp написать красивый загрузчик я незнаю, т.к. не знаю jsp.

Что значит "красивый"? Стандартный диалог загрузки файла делается в два счета http://www.htmlbook.ru/html/input.html (как например в форме быстрого ответа - прикрепить файл).

Автор: Gugle 18.3.2008, 18:03
Цитата

Про то что у клиента может быть не установлена JRE, или установленна старая версия.

Проблемы клиента. smile Жестоко, но это реально проблемы клиента.

Цитата

Что значит "красивый"? Стандартный диалог загрузки файла делается в два счета <input type="file" (как например в форме быстрого ответа - прикрепить файл).

Хммм... Почти уговорил. smile

А если честно интересно просто как можно это в аплете реализовать. Неужели передача файла так трудно?
Или методом post нельзя в апплете сформировать такую посылку как в html который представлен выше?

Автор: LSD 18.3.2008, 18:22
Цитата(Gugle @  18.3.2008,  18:03 Найти цитируемый пост)
А если честно интересно просто как можно это в аплете реализовать. Неужели передача файла так трудно?
Или методом post нельзя в апплете сформировать такую посылку как в html который представлен выше?

Да нет, если использовать например http://hc.apache.org/httpclient-3.x/index.html то элементарно:
Код

File f = new File("/path/fileToUpload.txt");
PostMethod filePost = new PostMethod("http://host/some_path");
Part[] parts = {
  new StringPart("param_name", "value"),
  new FilePart(f.getName(), f)
};
filePost.setRequestEntity(new MultipartRequestEntity(parts, filePost.getParams()));
HttpClient client = new HttpClient();
int status = client.executeMethod(filePost);

только это как микроскопом гвозди забивать.

Добавлено через 3 минуты и 15 секунд
Цитата(Gugle @  18.3.2008,  18:03 Найти цитируемый пост)
Жестоко, но это реально проблемы клиента.

На самом деле твои, ибо если не будет работать или клиент придет с вопросами к тебе. Или вообще уйдет к конкуренту.

Автор: Gugle 19.3.2008, 10:49
Цитата

На самом деле твои, ибо если не будет работать или клиент придет с вопросами к тебе. Или вообще уйдет к конкуренту.

smile Спорный вопрос и для другой темы.

Код

PostMethod filePost = new PostMethod("http://host/some_path");

Возвращает error:
Код

java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
       at org.apache.commons.httpclient.HttpMethodBase.<clinit>(HttpMethodBase.java:104)

Не могу понять что хочет. Вместо "http://host/some_path", ввел путь к своему серверу который должен принять файл. Он вообще никак не реагирует.

Автор: LSD 19.3.2008, 11:01
Цитата(Gugle @  19.3.2008,  10:49 Найти цитируемый пост)
Возвращает error:

http://hc.apache.org/httpclient-3.x/dependencies.html (junit нужен только для запуска тестов).

Автор: Gugle 19.3.2008, 12:13
Чего то я запутался.
Код

File f = new File("/path/fileToUpload.txt");
PostMethod filePost = new PostMethod("http://host/some_path"); // Создает POST запрос к серверу
Part[] parts = {
  new StringPart("param_name", "value"),
  new FilePart(f.getName(), f)
};
filePost.setRequestEntity(new MultipartRequestEntity(parts, filePost.getParams())); // Устанавливает настройки POST метода, т.е. что лежит в POST запросе.
HttpClient client = new HttpClient(); // Запрос от клиента.
int status = client.executeMethod(filePost); // Посылка запроса. По идее сервер должен отреагировать?

Автор: LSD 19.3.2008, 12:20
И что тебе не понятно? 
После выполнения executeMethod() ты получишь код ответа от сервера (коды описаны в HttpStatus) или ексепшн если сервер не ответит или ответ будет неправильным (не соответвовать спецификации HTTP протокола).

Автор: Gugle 19.3.2008, 12:36
Сервак НеАлё!!!

Добавлено @ 12:37
А статус что Алё.
Код статуса 200, т.е. ОК!

Автор: LSD 19.3.2008, 12:38
А ты уверен что сервак работает правильно?

Автор: Gugle 19.3.2008, 12:40
Так сервер отвечает при обращении к нему из формы. smile
Ладно чет с серваком. Пошел разбираться.

LSD Спасибо!

PS
На + нажал. smile

Автор: Gugle 19.3.2008, 15:35
Возвращаясь к баранам.
Можно как нить посмотреть сколько передано?

Автор: LSD 19.3.2008, 16:12
Создаешь свой класс унаследованный от FilePart, в нем переопределяешь метод sendData() где и считаешь количество переданых байт.

Автор: Gugle 20.3.2008, 12:15
А можно на сервере смотреть сколько получено и отправлять их клиенту во время приема данных?

Автор: LSD 20.3.2008, 12:59
Цитата(Gugle @  20.3.2008,  12:15 Найти цитируемый пост)
А можно на сервере смотреть сколько получено и отправлять их клиенту во время приема данных?

Не знаю, что ты используешь на сервере для приема. Но вообще такое реализовать можно.

Автор: Gugle 20.3.2008, 13:10
На серваке работает tomcat.
Принимаю вот так:
Код

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // Check that we have a file upload request
        if (FileUpload.isMultipartContent(request)) {
            ServletFileUpload upload = new ServletFileUpload(factory);
            String action = null;
            String fileName = null;
            FileItem actualFile = null;
            try {
                List  items = upload.parseRequest(request);
                Iterator iter = items.iterator();
                while (iter.hasNext()) {
                    FileItem item = (FileItem) iter.next();
                    if (item.isFormField()) {
                        // Если обычное поле, то мы можем получить его значение 
                        // в выбранной кодировке с помощью вызова 
                        // getString(нужная кодировка)
                        if (item.isFormField()) {
                            String name = item.getFieldName();
                            String value = item.getString();
                            // В данном случае араметр action
                            if (name.equals("action")) action = value;
                        }
                    } else {
                        // Если файл
                        if (!item.isFormField()) {
                            String fieldName = item.getFieldName();
                            String uploadedFileName = item.getName();
                            if (UPLOAD_FILE_FORM_NAME.equals(fieldName)) {
                                fileName = uploadedFileName;
                                actualFile = item;
                            }
                        }
                    }
                }
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


Принимаю файл с использование пакета fileupload.
Подскажи плз где надо поставить счетчик принятых байт.

Автор: LSD 20.3.2008, 14:26
Где тут у тебя непосредственно загрузка файла?
Ты из FileItem должен получить getInputStream() и когда будешь читать из него, считать количество принятых байт.

Автор: Gugle 20.3.2008, 15:18
Загрузка идет во время сохранения файла:
Код

File uploadedFile = new File(baseDir, fileName);
java.io.InputStream in = actualFile.getInputStream();
long size = actualFile.getSize();

actualFile.write(uploadedFile);


Эээ..  smile А как во время этой опирации байты подсмотреть?

Автор: LSD 20.3.2008, 15:24
Пиши вручную, InputStream у тебя уже есть, создай FileOutputStream и пиши туда. Заодно считай сколько записано байт.

Автор: Gugle 20.3.2008, 16:35
Вывод в консоль получил.
Еще пара вопросиков.

1. Вот тут веть то же происходит считывание данных?
Код

List items = upload.parseRequest(request);

Отловить процесс можно как нить?

2. На приемной стороне как получать ответы? Веть надо создать поток InputStream(), а
Код

PostMethod filePost = new PostMethod("http://host/some_path");


не возвращает входной поток. Нету такого метода у PostMetod
Код

java.io.InputStream in = filePost.getInputStream();


3. С сервера данные отправлять лучше через?
Код

java.io.ObjectOutputStream outb = new java.io.ObjectOutputStream(response.getOutputStream());

Автор: LSD 20.3.2008, 16:47
Цитата(Gugle @  20.3.2008,  16:35 Найти цитируемый пост)
Вот тут веть то же происходит считывание данных?

Нет не происходит.


Цитата(Gugle @  20.3.2008,  16:35 Найти цитируемый пост)
На приемной стороне как получать ответы?

Создав новое соединение. HTTP не позволяет в рамках одной сессии паралельно выпонлнять несколько запросов.


Цитата(Gugle @  20.3.2008,  16:35 Найти цитируемый пост)
С сервера данные отправлять лучше через?

Лучше или обычный HTTP или свой простенький протокол.

Автор: Gugle 20.3.2008, 17:02
А если я новое http соединение создам он разве будет принимать данные от соединения?
Код

PostMethod filePost = new PostMethod("http://host/some_path");


Добавлено через 1 минуту и 40 секунд
Сервер веть должен отвечать по этому соединению.

Автор: LSD 20.3.2008, 17:07
Цитата(Gugle @  20.3.2008,  17:02 Найти цитируемый пост)
Сервер веть должен отвечать по этому соединению.

Кому это он должен и сколько? smile 

Почитай уже описание того как работает HTTP.

Автор: Gugle 20.3.2008, 17:07
Веть при создании
Код

java.net.URLConnection conn = servlet.openConnection();

мы открываем вых поток
Код

java.io.DataOutputStream out = new java.io.DataOutputStream(conn.getOutputStream());

и должны получить входной для проверки буфера.

Автор: LSD 20.3.2008, 17:14
Ты получишь результат выполнения HTTP запроса, и только после того как он выполнится.

Цитата(LSD @  20.3.2008,  17:07 Найти цитируемый пост)
Почитай уже описание того как работает HTTP.

Автор: Gugle 20.3.2008, 17:54
Все. Понял о чем речь.
Прошу прощения за свою глупость...

З.Ы.
Ушел просвещаться.

Автор: Gugle 20.3.2008, 18:30
Просто да или нет. smile
Отправляя даннные, я могу посмотреть сколько я отправил, НО в этой "сессии" ("соединении") НЕ сколько принял.
Либо сколько я принял, НО в этой "сесии" я не могу сообщить клиенту сколько я принял ПОКА я не приму данные полностью.
Используя ОДИН метод POST.

Учитывая это я могу передавать данные методом POST, и в этой "сессии" клиент знает сколько данных НЕ ТОЛЬКО ПРИНЯТО НО И ПОЛУЧЕНО используя метод GET, ЕСЛИ сервер умеет сообщать об этом. Для этого я должен сервер научить так делать передая ему некоторый контент.

Автор: LSD 20.3.2008, 18:38
Вообще ничего не понял.

Автор: Gugle 20.3.2008, 19:07
Плохо. Значит из того что я прочитал я не сделал ни одного верного  вывода. smilesmilesmilesmile

Добавлено через 12 минут и 9 секунд
Значит. Сделав следующий запрос к сервеу:
Код

 PostMethod filePost = new PostMethod("http://localhost:8084/servlets-examples/fileReader");

 Я не могу получить от него ответа пока он не завершиться. Т.е. обязан дождаться передачи всего файлн и только после этого я получу подтверждение принял я его или нет.

Тогда как я могу получить подстверждение принятого байта?

З.Ы.
 Сорь. Ламер в этих вопросах.

Автор: LSD 20.3.2008, 19:38
Тебе надо или реализовать еще один сервлет, который по запросу будет возвращать количество байт загруженных в текущей сессии (т.е. тебе надо будет или хранить в сессии это число или сделать механизм который позволит его получить по ID сессии).

Либо вообще забить на POST, и реализовать свой сервлет (не HTTP!), который бы принимал данные по собственному протоколу и переодически слал бы данные о принятом количестве байт.

Автор: Gugle 25.3.2008, 10:21
Почему то не обновляется диалоговое окно прогресса в аплете. При этом если появление диалогового окна положить в метод init() аплета то все нормально, а если в обработчик нажатия на кнопку, то диалог появляется, но не обновляется.
Использую стандартное окно ProgressMonitor. В чем может быть проблема?

Автор: Gugle 25.3.2008, 10:55
Да, кстати, все расчеты происходят в отдельном потоке. (В данном случае отправка файла)

Автор: LSD 25.3.2008, 12:27
Покажи код.

Автор: Gugle 25.3.2008, 12:42
Applet:
Код

public class Main extends javax.swing.JApplet {

    public void init() {
        initComponents();
//        FileLoadToServer upload = new FileLoadToServer();
//        upload.upload(this, "D:/test.txt", utilPack.Util._path + "load/fileReader");
    }

    public void start() {
    }

    public void stop() {
    }

    public void destroy() {
    } 

    private void initComponents() {
        _panel = new ru.niir.gui.panel.BasesPanel();
        _fileChooser = new javax.swing.JFileChooser();

        _panel.add(_button);
        _button.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                _buttonActionPerformed(evt);
            }
        });
        add(_panel);
    }

    private void _buttonActionPerformed(java.awt.event.ActionEvent evt) {
        // TODO add your handling code here:
        int result = _fileChooser.showOpenDialog(this);
        switch (result) {
            case 0:
                String name = _fileChooser.getSelectedFile().getPath();
                FileLoadToServer upload = new FileLoadToServer();
                upload.upload(this, name, utilPack.Util._path + "load/fileReader");
                break;
            case 1:
            default:
        }
    }  

    // Variables declaration
    private javax.swing.JPanel _panel;
    private javax.swing.JButton _button;
    private javax.swing.JFileChooser _fileChooser;
    // End of variables declaration
}


Код загрузчика:
Код

public class FileLoadToServer {

    public FileLoadToServer () {
    }

    /**
     * Метод FilePart() переписан.
     * @param parent родительский класс
     * @param aFile путь к файлу
     * @param aServer путь к серверу
     * @return статус передачи по ее истечении.
     */
    public String upload(java.awt.Component parent, String aFile, String aServer) {
       String status = null;
        try {
            File f = new File(aFile);
            FilePart filePart = new FilePart("filename", f);
            PostMethod filePost = new PostMethod(aServer);
            Part[] parts = {
                new StringPart("action", "uploadfile"),
                filePart
            };
            MultipartRequestEntity entiti = new MultipartRequestEntity(parts, filePost.getParams());
            filePost.setRequestEntity(entiti);
            
            int i = (int)entiti.getContentLength();

            // Обнаружение фрейма владельца.
            Applet owner = null;
            if (parent instanceof Applet) owner = (Applet) parent;
            else owner = (Applet)SwingUtilities.getAncestorOfClass(Applet.class, parent);
            javax.swing.ProgressMonitor pm = new javax.swing.ProgressMonitor(
                   owner , "Загрузка - " + f.getName(), "", 0, i);
            pm.setProgress(0);
            pm.setMillisToPopup(100);

            //Запускаем поток передачи файла
            quiryfile qf = new quiryfile(filePost);
            qf.start();

            // Пока поток работает делаем обновление
            while (!qf.getShutDown()) {
                int progress = filePart.getTotalRead();
                String message = String.format("Передано %,d байт из %,d ", progress, i);
                pm.setNote(message);
                pm.setProgress(progress);
            }
            qf.setShutDown();
            java.awt.Toolkit.getDefaultToolkit().beep();
            pm.close();
            status = qf.getStatus();

            filePost.releaseConnection();
        } catch (IOException e) {
          System.err.println("Fatal transport error: " + e.getMessage());
          e.printStackTrace();
        }
        return status;
    }
}

Автор: LSD 25.3.2008, 13:19
Ну и где тут у тебя отправка происходит в отдельно потоке? Все работает в AWT потоке, потому ничего и не перерисовывается. Вынеси загрузку файла на сервер в отдельный поток.

Автор: Gugle 25.3.2008, 14:50
Вынес все в отдельный поток.
Код

public class FileLoadToServer {

    public FileLoadToServer () {
    }

    public String upload(java.awt.Component parent, String aFile, String aServer) {
        String status = null;
            // Обнаружение фрейма владельца.
            Applet owner = null;
            if (parent instanceof Applet) owner = (Applet) parent;
            else owner = (Applet)SwingUtilities.getAncestorOfClass(Applet.class, parent);

            quiryfile qf = new quiryfile(aFile, aServer);
            qf.start();
            
            while (!qf.getExecute()) {
                // Тупое ожидание начала передачи
            }
            
Код



            int i = (int)qf.getContentLength();
            javax.swing.ProgressMonitor pm = new javax.swing.ProgressMonitor(
                   owner , "Загрузка - " + f.getName(), "", 0, i);
            pm.setProgress(0);
            
            while (!qf.getShutDown()) {
                int progress = qf.getTotalRead();
                String message = String.format("Передано %,d байт из %,d ", progress, i);
                pm.setNote(message);
                pm.setProgress(progress);
                System.out.println(message);
            }
            status = qf.getStatus();
            qf.setShutDown();
            java.awt.Toolkit.getDefaultToolkit().beep();
            pm.close();
        return status;
    }
}


class quiryfile extends Thread {

    public quiryfile(String aFile, String aServer) {
        _file = aFile;
        _server = aServer;
    }


    public void run() {
        while (!done) {
            try {
                File f = new File(_file);
                _filePart = new FilePart("filename", f);
                PostMethod filePost = new PostMethod(_server);
                Part[] parts = {
                    new StringPart("action", "uploadfile"),
                    _filePart
                };
                _entiti = new MultipartRequestEntity(parts, filePost.getParams());
                filePost.setRequestEntity(_entiti);
                _execute = true;
                HttpClient client = new HttpClient();
                int status = client.executeMethod(filePost);
                if (status != HttpStatus.SC_OK) _status = "Method failed: " + filePost.getStatusLine();
                else _status = "OK";
                
                filePost.releaseConnection();
            } catch (IOException ex) {
                Logger.getLogger(quiryfile.class.getName()).log(Level.SEVERE, null, ex);
            }
            setShutDown();
        }
    }

    public void setShutDown() {
        done = true;
    }

    public boolean getShutDown() {
        return done;
    }

    public long getContentLength() {
        return _entiti.getContentLength();
    }

    public int getTotalRead() {
        return _filePart.getTotalRead();
    }

    public boolean getExecute() {
        return _execute;
    }

    private MultipartRequestEntity _entiti;
    private String _status = null;
    private FilePart _filePart;
    protected boolean done = false;
    protected String _file;
    protected String _server;
    protected boolean _execute = false;
}

Ничего не изменилось.
Или я не верно делаю?

Автор: LSD 25.3.2008, 15:25
Блин!
Ну нафига надо ожидать пока тот поток завершиться? В чем тогда вообще смысл выносить выполнение это операции в отдельный поток, если основной поток все равно будет ждать пока вспомогательный завершиться?

Ты должен запустить поток и выйти з метода. Когда поток завершит загрузку пусть вызовет некий метод который сделает необходимые действия (покажет сообщение и т.п.). В этом же потоке обновляй значение прогресбара по мере продвижения загрузки. Только не забывай, что все изменения GUI из других потоков надо делать через SwingUtilities.invokeLater().

Автор: Gugle 25.3.2008, 15:59
Цитата

Ну нафига надо ожидать пока тот поток завершиться? В чем тогда вообще смысл выносить выполнение это операции в отдельный поток, если основной поток все равно будет ждать пока вспомогательный завершиться?

Так основной поток рождает диалоговое окно которое говорит пользователю, что он должен ожидать окончания загрузки файла. При этом я должен как то заполнять progressBar, вот я и создаю доп. поток который передает файл и при этом из которого я вытаскиваю количество переданной информации. Основной поток при этом должен так же ожидать загрузки файла на сервер.
Разве  при этом обновление progressBar рожденного в основном потоке не должно происходить?

Цитата

Ты должен запустить поток и выйти з метода. Когда поток завершит загрузку пусть вызовет некий метод который сделает необходимые действия (покажет сообщение и т.п.). В этом же потоке обновляй значение прогресбара по мере продвижения загрузки. Только не забывай, что все изменения GUI из других потоков надо делать через SwingUtilities.invokeLater().

А зачем мне ждать когда поток завершит загрузку? Веть мне надо показать progressBar именно во время загрузки файла.

Добавлено через 1 минуту и 14 секунд
А так же показывать сколько % загружено.

Автор: LSD 25.3.2008, 17:45
Ты вызываешь загрузку из обработчика события AWT. Пока ты не выйдешь из этого обработчика не будет происходить никакая обработка событий GUI, ничего не будет перерисовываться, окошки не будут открываться/закрываться и т.д. Именно для этого и нужен отдельный поток, чтобы сразу после его запуска вернуть управление AWT.

Автор: Gugle 26.3.2008, 12:27
Спасибки.

Автор: emelanov 8.4.2008, 22:38
LSD,
  День добрый,
 у меня такая же проблема, только вместо апплета вебстарт
 (т.е. вебстарт-эдитор для определённого вида xml-файлов, 
 должен уметь сохранять готовые на родном сервере), 
 думаю воспользоваться этим же решением.
Вопрос: это тоже будет :"
 
Цитата

только это как микроскопом гвозди забивать.
"
т.е. существует ли более красивое решение?

Автор: LSD 9.4.2008, 11:19
Цитата(emelanov @  8.4.2008,  23:38 Найти цитируемый пост)
Вопрос: это тоже будет: "только это как микроскопом гвозди забивать." т.е. существует ли более красивое решение?

В твоем случае - нет не будет. Дело в том, что Gugle, делал апплет предназначенный только для загрузки файлов на сервер, хотя подобный функционал можно реализовать и без применения апплета.

У тебя же есть готовое приложение и ты просто добавляешь в него некий функционал. Что лучше использовать HTTP POST или свой собственный протокол - вопрос открытый. С одной стороны HTTP POST проще реализовывать серверную часть и нет привязки к языку программирования, очень нетребователен к соединению (если клиент сидит за firewall, proxy, NAT). Свой протоколо - более гибкий (например позволяет реализовать докачку, сжатие передаваемых данных и т.п.), позволяет обойтись одним соединением для получения прогресса загрузки (в случае с HTTP POST, нужно одно для загрузки, и еще куча запросов на выяснение сколько было уже загружено).

Автор: emelanov 9.4.2008, 11:38
ок, спасибо.

Думаю, мне HTTP POST подойдёт, так как мне не нужны проценты, файлы будут довольно мелкие, по любому меньше Mb.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)