5 Replies Latest reply on Oct 23, 2007 10:36 PM by zaya

    Progress bar for fileUpload

    zaya

      This is the use case:

      The website has users to upload very large files (probably videos), I'm trying to trace the course of the uploading by representing in browser a progress bar. But I don't know if I can do that by using s:fileUpload and how.

      By my understanding, s:fileUpload tag uses a filter to store the input file, and map that file to an InputStream. My concern is, because UpdateModel jsf phase is very late in the lifecycle, at the time I have accessibility to the mapped InputStream, the file has already fully uploaded to some temporary place, and I can only use this InputStream to store the file elsewhere, and I lose the control in the course of the uploading.

      Anyone has a suggestion?

        • 1. Re: Progress bar for fileUpload
          shane.bryzak

          I was thinking of tracking file upload progress as a session-scoped variable, but this is not the most desirable solution (a user could theoretically upload more than one file at once). Feel free to raise an issue for this in JIRA, that way at least I can keep track of it until I have time to come up with a better idea.

          • 2. Re: Progress bar for fileUpload
            zaya

            Thanks shane.

            Do you mean a user will upload several files in a _single_ multipart request, or upload several files simultaneously in different tabs of browser in the same user session but _separated_ multipart requests?

            If what you means is the first. I guess it's just not possible to distinguish individually how much each file is uploaded since in http, fields are serialized and it doesn't make sense to distinguish so. Just use session variable to store how much the total bytes the multipart request is, and how much bytes currently the filter has received, that should be perfectly enough, at least for our use case.

            If what you mean it the second one, I think it's much better to store variables in conversation scoped components, and these variables will not interfere each other because they are isolated by conversion. This is a very interesting feature I admit, and if seam can implement it it would be very nice.

            Before jira can solve this issue, I'd like to come up to some quick solutions. Now I have two scenarios:

            1. Build a servlet myself and use that servlet to process file uploads.
            2. Extends SeamFilter

            The second one should be cleaner, but I don't how difficulte it would be.

            Both need to put some variables( eventually, total bytes for a request and currently processed bytes for that request) somewhere. And both require to access seam component (of course, I can use session but it's not clean anyway) in servlet or servlet filter. Is there way to access seam component in servlets or filters? Then I can find them using a4j or seam remoting.

            Cheers,
            Zaya

            • 3. Re: Progress bar for fileUpload
              zaya
              • 4. Re: Progress bar for fileUpload
                zaya

                A temporary solution:

                Overide multipartFilter to put multipartRequest into session(Put this class anywhere in application source):

                @Startup
                @Scope(APPLICATION)
                @Name("org.jboss.seam.web.multipartFilter")
                @Install(precedence = Install.APPLICATION)
                @BypassInterceptors
                @Filter(within={"org.jboss.seam.web.ajax4jsfFilter", "org.jboss.seam.web.exceptionFilter"})
                public class MonitoredMultipartFilter extends MultipartFilter {
                
                 @Override
                 public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
                 throws IOException, ServletException {
                 if (!(response instanceof HttpServletResponse)) {
                 chain.doFilter(request, response);
                 return;
                 }
                
                 final HttpServletRequest httpRequest = (HttpServletRequest) request;
                
                 if (isMultipartRequest(httpRequest)) {
                 processMultipart(httpRequest, response, chain);
                 } else chain.doFilter(request, response);
                 }
                
                 private void processMultipart(HttpServletRequest httpRequest, ServletResponse response, FilterChain chain)
                 throws IOException, ServletException {
                
                 MultipartRequest multipartRequest = new MultipartRequest(httpRequest, getCreateTempFiles(), getMaxRequestSize());
                 HttpSession httpSession = httpRequest.getSession(false);
                 if (httpSession != null) {
                 httpSession.setAttribute("MULITIPART_REQUESET", multipartRequest);
                 }
                
                 chain.doFilter(multipartRequest, response);
                
                 }
                
                 private boolean isMultipartRequest(HttpServletRequest request) {
                 if (!"post".equals(request.getMethod().toLowerCase())) {
                 return false;
                 }
                
                 String contentType = request.getContentType();
                 if (contentType == null) {
                 return false;
                 }
                
                 if (contentType.toLowerCase().startsWith(MULTIPART)) {
                 return true;
                 }
                
                 return false;
                 }
                }
                


                And in the backing seam component:
                 @In(value="MULITIPART_REQUESET",required=false) private MultipartRequest multipartRequest;
                 private static final String UPLOAD_COMPONENT_ID_IN_HTML = "uploadForm:upload";
                
                 @WebRemote
                 public Integer uploadPercentage() {
                 int uploadsize = multipartRequest.getFileSize(UPLOAD_COMPONENT_ID_IN_HTML);
                 int totalsize = multipartRequest.getContentLength();
                 if (uploadsize>=0 && totalsize>=0) {
                 Integer progress = (int)Math.floor(((double)uploadsize / (double) totalsize) * 100.0);
                 return progress;
                 } else return null;
                 }
                


                Now seam remoting can access uploadPercentage() method to get the progress.

                • 5. Re: Progress bar for fileUpload
                  zaya

                  But I still have question on this solution:

                  1.I want to put the attribute into conversation instead of session. I tried to use ContextualHttpServletRequest to wrap the processMulitpart() method in the filter to access conversation context. But it's no success. It gives error "java.lang.IllegalStateException: No active event context".
                  Maybe it's becauce seam context is built twice?

                  2. In the mulitpart request value of getContentLength() method doesn't exactly match the size of a fully uploaded file, even when only one uploaded file exists. This makes difficult to examine if the upload is really finished.

                  Thanks in advance.