使用RichFaces下载文件

时间:2012-02-01 16:05:37

标签: java jsp jsf richfaces

我已经完成了以下工作:

  1. 用户可以上传文件(即压缩档案)
  2. 用户可以解压缩服务器上的文件
  3. 用户可以对这些文件执行一些操作,从而生成更多文件
  4. 现在我需要让第4步工作:

    • 用户可以再次将文件下载到自己的计算机上

    任何人都可以给我一个提示吗?我试图了解我在谷歌上发现的东西,但它并没有像预期的那样发挥作用。我是否必须设置内容类型?当我设置应用程序/八位字节流时​​,只有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 个答案:

答案 0 :(得分:8)

您的具体问题是您正在尝试通过Ajax下载文件。这是不正确的。 JavaScript无法处理二进制响应,也没有任何强制另存为对话的工具。你需要让它成为一个正常的同步请求,这样它就是webbrowser本身必须处理它的。

<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解析它(服务器的默认的一个或你的webapp的一个。)

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

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

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

(请注意,我假设你正在使用JSF 1.x,看看你如何获得servlet响应,你可以,因为JSF 2.x否则也使用ExternalContext#getMimeType()

答案 1 :(得分:4)

我几周前完成了第4步,让我给你一些建议:

  1. 使用链接html标记组件。为此,我建议使用a4j:htmlCommandLink标记组件(它与公共h:commandLink类似,区别在于<f:param />组件始终呈现,您可以在component documentation中查看更多内容

  2. 如果您不知道要下载的文件类型(),则必须将内容设置为 application / octet-stream

  3. 在设置要下载到您的回复的文件后,您应该设置回复已完成。

  4. 我会将此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中进行了更新和测试。对于缓冲区大小常数的值,我做了一些研究并在this站点找到了一个很好的答案。

    P.S。:我不认为这是一场比赛,我只是尽力帮助别人。如果我的代码不好,那么感谢让我知道,这是让每个人都能更好,更健康地成长的更好方式:)。


    编辑:感谢@lisa

    要手动完成以下操作,只需在代码段中更改此部分

    即可
    //strNombreCompletoArchivo = UManejadorSesionWeb.obtieneParametro("nombreCompletoArchivo");
    String parameterStr = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap.get("nombreCompletoArchivo");
    strNombreCompletoArchivo = parameterStr;
    

答案 2 :(得分:3)

下载时不需要richfaces a4j:commandLink标记,标准的jsf标记h:commandLink就足够了。

然后确保在响应中设置了以下标题(您可以在firefox中使用firebug进行检查):

  

内容 - 处置附件;文件名= “your_file_name.xxx”

     

Content-Type application / xxx

     

内容长度1234

Content-Length:字节数。