File download using RichFaces

前端 未结 3 1637
渐次进展
渐次进展 2020-12-20 20:59

I got the following to work already:

  1. User can upload a file (i.e. a compressed archive)
  2. User can uncompress the file on the server
  3. User can e
相关标签:
3条回答
  • 2020-12-20 21:24

    Your concrete problem is that you're attempting to download files by Ajax. This is not correct. JavaScript can't deal with binary responses nor has it any facilities to force a Save As dialogue. You need to make it a normal synchronous request instead so that it's the webbrowser itself who has to deal with it.

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

    As to setting the content type, if you have a file name with extension at your hands, you could use ServletContext#getMimeType() to resolve it based on <mime-mapping> in web.xml (either the server's default one or your webapp's one).

    ServletContext servletContext = (ServletContext) externalContext.getContext();
    String contentType = servletContext.getMimeType(file.getName());
    
    if (contentType == null) {
        contentType = "application/octet-stream";
    }
    
    response.setContentType(contentType);
    // ...
    

    (note that I assume that you're using JSF 1.x, seeing the way how you obtained the servlet response, you could since JSF 2.x otherwise also use ExternalContext#getMimeType())

    0 讨论(0)
  • 2020-12-20 21:26

    I have done the step 4 some weeks ago, and let me give you some advices:

    1. Use a link html tag component. For this, I recommend the a4j:htmlCommandLink tag component (it's like the common h:commandLink with the difference that the <f:param /> components are always rendered, you can check more in the component documentation).

    2. If you don't know the type of the file to download (), then you must set the Content as application/octet-stream.

    3. After setting up the file to download to your response, you should set that the response has been completed.

    I'll put my Backing Bean code for this request:

    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;
    }
    

    The jsp fragment I used looks like this:

    <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>
    

    Hope this helps you.

    EDIT: I had some spare time, sorry but we are kinda busy in the project. The Java code was updated and tested in IE8, Firefox 9 and 10, and Chrome 16. For the value of buffer size constant, I did some research and found a good answer in this site.

    P.S.: I don't take this as a competition, I just try to help people when I can. If my code is not good then thanks for letting me know it, that's the better way for everyone to grow better and healthy :).


    EDIT: Thanks to @lisa

    To achieve the following manually, just changing this part in the snippet

    //strNombreCompletoArchivo = UManejadorSesionWeb.obtieneParametro("nombreCompletoArchivo");
    String parameterStr = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap.get("nombreCompletoArchivo");
    strNombreCompletoArchivo = parameterStr;
    
    0 讨论(0)
  • 2020-12-20 21:32

    You don't need the richfaces a4j:commandLink tag for the download, the standard jsf tag h:commandLink would be enough.

    Then make sure you have the following headers set on your response (you can check with firebug in firefox):

    Content-Disposition attachment; filename="your_file_name.xxx"

    Content-Type application/xxx

    Content-Length 1234

    Content-Length: number of bytes.

    0 讨论(0)
提交回复
热议问题