Загрузка файла с использованием RichFaces - PullRequest
6 голосов
/ 01 февраля 2012

У меня уже работает следующее:

  1. Пользователь может загрузить файл (т.е. сжатый архив)
  2. Пользователь может распаковать файл на сервере
  3. Пользователь может выполнить некоторые действия с этими файлами, в результате чего будет создано больше файлов

Теперь мне нужно заставить шаг 4 работать:

  • Пользователь может снова загрузить файлы на свой компьютер

Может кто-нибудь дать мне подсказку? Я пытался понять материал, который нашел в Google, но он не работает так, как ожидалось. Нужно ли устанавливать тип контента? Когда я устанавливаю поток application / octet, только txt и csv файлы будут отображаться правильно (в браузере, а не во всплывающем окне загрузки, как я хотел), другие файлы не будут работать ...

JSP:

<a4j:commandLink value="Download" action="#{appController.downloadFile}" rendered="#{!file.directory}">
   <f:param name="file" value="#{file.absoluteFilename}" />
</a4j:commandLink>

AppController:

public String downloadFile() {
    String filename = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("file");
    File file = new File(filename);
    HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();  

    writeOutContent(response, file, file.getName());

    FacesContext.getCurrentInstance().responseComplete();
    return null;
}

private void writeOutContent(final HttpServletResponse res, final File content, final String theFilename) {
    if (content == null) {
        return;
    }
    try {
        res.setHeader("Pragma", "no-cache");
        res.setDateHeader("Expires", 0);
        res.setHeader("Content-disposition", "attachment; filename=" + theFilename);
        FileInputStream fis = new FileInputStream(content);
        ServletOutputStream os = res.getOutputStream();
        int bt = fis.read();
        while (bt != -1) {
            os.write(bt);
            bt = fis.read();
        }
        os.flush();
        fis.close();
        os.close();
    } catch (final IOException ex) {
        Logger.getLogger(ApplicationController.class.getName()).log(Level.SEVERE, null, ex);
    }
}

Ответы [ 3 ]

8 голосов
/ 01 февраля 2012

Ваша конкретная проблема в том, что вы пытаетесь загрузить файлы с помощью Ajax.Это не правильно.JavaScript не может работать с бинарными ответами и не имеет никаких средств для запуска диалога Сохранить как .Вместо этого вам нужно сделать обычный синхронный запрос, чтобы его обрабатывал сам веб-браузер.

<h:commandLink value="Download" action="#{appController.downloadFile}" rendered="#{!file.directory}">
   <f:param name="file" value="#{file.absoluteFilename}" />
</h:commandLink>

Что касается настройки типа контента, если у вас в руках имя файла с расширением, вы можете использовать ServletContext#getMimeType(), чтобы разрешить его на основе <mime-mapping> в web.xml (либо по умолчанию для сервера, либо для вашего веб-приложения).

ServletContext servletContext = (ServletContext) externalContext.getContext();
String contentType = servletContext.getMimeType(file.getName());

if (contentType == null) {
    contentType = "application/octet-stream";
}

response.setContentType(contentType);
// ...

(обратите внимание, что я предполагаю, что вывы используете JSF 1.x, видя, как вы получили ответ сервлета, вы могли бы, так как JSF 2.x в противном случае также использует ExternalContext#getMimeType())

4 голосов
/ 01 февраля 2012

Я сделал шаг 4 несколько недель назад, и позвольте мне дать вам несколько советов:

  1. Используйте компонент html tag ссылки.Для этого я рекомендую компонент тега a4j:htmlCommandLink (он похож на обычный h:commandLink с той разницей, что компоненты <f:param /> всегда отображаются, вы можете проверить больше в документации компонента ).

  2. Если вы не знаете тип файла для загрузки (), вы должны установить Контент как application / octet-stream .

  3. После настройки файла для загрузки в ваш ответ, вы должны установить, что ответ был завершен.

Для этого я добавлю свой код Backing Beanзапрос:

public void descargaArchivo() {
    //sorry but the programming standard says that we MUST declare
    //our variables at the beginning of any function =(
    HttpServletResponse objResponse;
    FileInputStream objFileInputStream;
    String strNombreCompletoArchivo, strNombreArchivo;
    byte[] arrDatosArchivo;
    try {
        //Here I get the <f:param> with the full name of the file. It encapsulates
        // the Faces.getCurrentInstance...  call.
        strNombreCompletoArchivo = UManejadorSesionWeb.obtieneParametro("nombreCompletoArchivo");
        //The function obtieneNombreArchivo retrieves the name of the file
        //based on the full name and the file separator (/ for Windows, \ for Linux)
        strNombreArchivo = UFuncionesGenerales.obtieneNombreArchivo(strNombreCompletoArchivo);
        //Getting the response from Faces.getCurrentInstance... 
        objResponse = UManejadorSesionWeb.obtieneHttpResponse();
        //Setting up the response content type and header (don't set the length!)
        objResponse.setContentType("application/octet-stream");
        objResponse.setHeader("Content-Disposition", "attachment; filename=\"" + strNombreArchivo + "\"");
        //Create the FileInputStream for the file to download
        objFileInputStream = new FileInputStream(strNombreCompletoArchivo);
        //Setting the file on the response
        arrDatosArchivo = new byte[UConstante.BUFFER_SIZE];
        while(objFileInputStream.read(arrDatosArchivo, 0, UConstante.BUFFER_SIZE) != -1) {
           objResponse.getOutputStream().write(arrDatosArchivo, 0, UConstante.BUFFER_SIZE);
        }
        objFileInputStream.close();
        objResponse.getOutputStream().flush();
        objResponse.getOutputStream().close();
        //Telling the framework that the response has been completed.
        FacesContext.getCurrentInstance().responseComplete();
    } catch (Exception objEx) {
        //manage the errors...
    }
}
//The constant used for byte array size
public class UConstante {
    public static final int BUFFER_SIZE = 2048;
}

Фрагмент jsp, который я использовал, выглядит следующим образом:

<a4j:htmlCommandLink  rendered="#{documento.rutaDestino != null}"
    action="#{documentoRequerido.descargaArchivo}">
    <f:param name="nombreCompletoArchivo" value="#{documento.rutaDestino}" />
    <h:graphicImage value="/Resource/iconos/mover-abajo.png" styleClass="pic" />
</a4j:htmlCommandLink>

Надеюсь, это поможет вам.

РЕДАКТИРОВАТЬ: у меня было немного свободного времени, извините, номы немного заняты в проекте.Код Java был обновлен и протестирован в IE8, Firefox 9 и 10 и Chrome 16. Что касается значения константы размера буфера, я провел небольшое исследование и нашел хороший ответ на этом сайте.

PS: Я не воспринимаю это как соревнование, я просто стараюсь помогать людям, когда могу.Если мой код не очень хорош, спасибо, что сообщили мне об этом, это лучший способ для всех расти лучше и здоровее:).


РЕДАКТИРОВАТЬ: Благодаря @ lisa

Toдобиться следующего вручную, просто изменив эту часть во фрагменте

//strNombreCompletoArchivo = UManejadorSesionWeb.obtieneParametro("nombreCompletoArchivo");
String parameterStr = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap.get("nombreCompletoArchivo");
strNombreCompletoArchivo = parameterStr;
3 голосов
/ 01 февраля 2012

Вам не нужен тег richfaces a4j: commandLink для загрузки, достаточно стандартного тега jsf h: commandLink.

Затем убедитесь, что в вашем ответе установлены следующие заголовки (вы можете проверить это с помощью firebug в firefox):

Приложение Content-Disposition; имя файла = "your_file_name.xxx"

Приложение типа контента / xxx

Контент-длина 1234

Content-Length: количество байтов.

...