美文网首页文件系统相关
Java http大文件断点续传上传

Java http大文件断点续传上传

作者: chjxidian | 来源:发表于2019-04-18 17:17 被阅读0次

    1,项目调研

    因为需要研究下断点上传的问题。找了很久终于找到一个比较好的项目。

    在GoogleCode上面,代码弄下来超级不方便,还是配置hosts才好,把代码重新上传到了github上面。

    https://github.com/freewebsys/java-large-file-uploader-demo

    效果:上传中,显示进度,时间,百分比。

    点击【Pause】暂停,点击【Resume】继续。

    2,代码分析

    原始项目:https://code.google.com/p/java-large-file-uploader/

    这个项目最后更新的时间是 2012 年,项目进行了封装使用最简单的方法实现了http的断点上传。

    因为html5 里面有读取文件分割文件的类库,所以才可以支持断点上传,所以这个只能在html5 支持的浏览器上面展示。

    同时,在js 和 java 同时使用 cr32 进行文件块的校验,保证数据上传正确。

    代码在使用了最新的servlet 3.0 的api,使用了异步执行,监听等方法。

    上传类UploadServlet

    @Component("javaLargeFileUploaderServlet")

        @WebServlet(name = "javaLargeFileUploaderServlet", urlPatterns = { "/javaLargeFileUploaderServlet" }) 

        public class UploadServlet extends HttpRequestHandlerServlet 

                implements HttpRequestHandler {  

            private static final Logger log = LoggerFactory.getLogger(UploadServlet.class);  

            @Autowired 

            UploadProcessor uploadProcessor;  

            @Autowired 

            FileUploaderHelper fileUploaderHelper;  

            @Autowired 

            ExceptionCodeMappingHelper exceptionCodeMappingHelper;  

            @Autowired 

            Authorizer authorizer;  

            @Autowired 

            StaticStateIdentifierManager staticStateIdentifierManager;  

            @Override 

            public void handleRequest(HttpServletRequest request, HttpServletResponse response) 

                    throws IOException { 

                log.trace("Handling request");  

                Serializable jsonObject = null; 

                try { 

                    // extract the action from the request 

                    UploadServletAction actionByParameterName = 

                            UploadServletAction.valueOf(fileUploaderHelper.getParameterValue(request, UploadServletParameter.action));  

                    // check authorization 

                    checkAuthorization(request, actionByParameterName);  

                    // then process the asked action 

                    jsonObject = processAction(actionByParameterName, request);  

                    // if something has to be written to the response 

                    if (jsonObject != null) { 

                        fileUploaderHelper.writeToResponse(jsonObject, response); 

                    }  

                } 

                // If exception, write it 

                catch (Exception e) { 

                    exceptionCodeMappingHelper.processException(e, response); 

                }  

            }  

            private void checkAuthorization(HttpServletRequest request, UploadServletAction actionByParameterName) 

                    throws MissingParameterException, AuthorizationException {  

                // check authorization 

                // if its not get progress (because we do not really care about authorization for get 

                // progress and it uses an array of file ids) 

                if (!actionByParameterName.equals(UploadServletAction.getProgress)) {  

                    // extract uuid 

                    final String fileIdFieldValue = fileUploaderHelper.getParameterValue(request, UploadServletParameter.fileId, false);  

                    // if this is init, the identifier is the one in parameter 

                    UUID clientOrJobId; 

                    String parameter = fileUploaderHelper.getParameterValue(request, UploadServletParameter.clientId, false); 

                    if (actionByParameterName.equals(UploadServletAction.getConfig) && parameter != null) { 

                        clientOrJobId = UUID.fromString(parameter); 

                    } 

                    // if not, get it from manager 

                    else { 

                        clientOrJobId = staticStateIdentifierManager.getIdentifier(); 

                    }  

                    // call authorizer 

                    authorizer.getAuthorization( 

                            request, 

                            actionByParameterName, 

                            clientOrJobId, 

                            fileIdFieldValue != null ? getFileIdsFromString(fileIdFieldValue).toArray(new UUID[] {}) : null);  

                } 

            }  

            private Serializable processAction(UploadServletAction actionByParameterName, HttpServletRequest request) 

                    throws Exception { 

                log.debug("Processing action " + actionByParameterName.name());  

                Serializable returnObject = null; 

                switch (actionByParameterName) { 

                    case getConfig: 

                        String parameterValue = fileUploaderHelper.getParameterValue(request, UploadServletParameter.clientId, false); 

                        returnObject = 

                                uploadProcessor.getConfig( 

                                        parameterValue != null ? UUID.fromString(parameterValue) : null); 

                        break; 

                    case verifyCrcOfUncheckedPart: 

                        returnObject = verifyCrcOfUncheckedPart(request); 

                        break; 

                    case prepareUpload: 

                        returnObject = prepareUpload(request); 

                        break; 

                    case clearFile: 

                        uploadProcessor.clearFile(UUID.fromString(fileUploaderHelper.getParameterValue(request, UploadServletParameter.fileId))); 

                        break; 

                    case clearAll: 

                        uploadProcessor.clearAll(); 

                        break; 

                    case pauseFile: 

                        List<UUID> uuids = getFileIdsFromString(fileUploaderHelper.getParameterValue(request, UploadServletParameter.fileId)); 

                        uploadProcessor.pauseFile(uuids); 

                        break; 

                    case resumeFile: 

                        returnObject = 

                                uploadProcessor.resumeFile(UUID.fromString(fileUploaderHelper.getParameterValue(request, UploadServletParameter.fileId))); 

                        break; 

                    case setRate: 

                        uploadProcessor.setUploadRate(UUID.fromString(fileUploaderHelper.getParameterValue(request, UploadServletParameter.fileId)), 

                                Long.valueOf(fileUploaderHelper.getParameterValue(request, UploadServletParameter.rate))); 

                        break; 

                    case getProgress: 

                        returnObject = getProgress(request); 

                        break; 

                } 

                return returnObject; 

            }  

            List<UUID> getFileIdsFromString(String fileIds) { 

                String[] splittedFileIds = fileIds.split(","); 

                List<UUID> uuids = Lists.newArrayList(); 

                for (int i = 0; i < splittedFileIds.length; i++) { 

                    uuids.add(UUID.fromString(splittedFileIds[i])); 

                } 

                return uuids; 

            }  

            private Serializable getProgress(HttpServletRequest request) 

                    throws MissingParameterException { 

                Serializable returnObject; 

                String[] ids = 

                        new Gson() 

                                .fromJson(fileUploaderHelper.getParameterValue(request, UploadServletParameter.fileId), String[].class); 

                Collection<UUID> uuids = Collections2.transform(Arrays.asList(ids), new Function<String, UUID>() {  

                    @Override 

                    public UUID apply(String input) { 

                        return UUID.fromString(input); 

                    }  

                }); 

                returnObject = Maps.newHashMap(); 

                for (UUID fileId : uuids) { 

                    try { 

                        ProgressJson progress = uploadProcessor.getProgress(fileId); 

                        ((HashMap<String, ProgressJson>) returnObject).put(fileId.toString(), progress); 

                    } 

                    catch (FileNotFoundException e) { 

                        log.debug("No progress will be retrieved for " + fileId + " because " + e.getMessage()); 

                    } 

                } 

                return returnObject; 

            }  

            private Serializable prepareUpload(HttpServletRequest request) 

                    throws MissingParameterException, IOException {  

                // extract file information 

                PrepareUploadJson[] fromJson = 

                        new Gson() 

                                .fromJson(fileUploaderHelper.getParameterValue(request, UploadServletParameter.newFiles), PrepareUploadJson[].class);  

                // prepare them 

                final HashMap<String, UUID> prepareUpload = uploadProcessor.prepareUpload(fromJson);  

                // return them 

                return Maps.newHashMap(Maps.transformValues(prepareUpload, new Function<UUID, String>() {  

                    public String apply(UUID input) { 

                        return input.toString(); 

                    }; 

                })); 

            }  

            private Boolean verifyCrcOfUncheckedPart(HttpServletRequest request) 

                    throws IOException, MissingParameterException, FileCorruptedException, FileStillProcessingException { 

                UUID fileId = UUID.fromString(fileUploaderHelper.getParameterValue(request, UploadServletParameter.fileId)); 

                try { 

                    uploadProcessor.verifyCrcOfUncheckedPart(fileId, 

                            fileUploaderHelper.getParameterValue(request, UploadServletParameter.crc)); 

                } 

                catch (InvalidCrcException e) { 

                    // no need to log this exception, a fallback behaviour is defined in the 

                    // throwing method. 

                    // but we need to return something! 

                    return Boolean.FALSE; 

                } 

                return Boolean.TRUE; 

            } 

        } 

    异步上传UploadServletAsync

    @Component("javaLargeFileUploaderAsyncServlet")

        @WebServlet(name = "javaLargeFileUploaderAsyncServlet", urlPatterns = { "/javaLargeFileUploaderAsyncServlet" }, asyncSupported = true) 

        public class UploadServletAsync extends HttpRequestHandlerServlet 

                implements HttpRequestHandler {  

            private static final Logger log = LoggerFactory.getLogger(UploadServletAsync.class);  

            @Autowired 

            ExceptionCodeMappingHelper exceptionCodeMappingHelper;  

            @Autowired 

            UploadServletAsyncProcessor uploadServletAsyncProcessor;  

            @Autowired 

            StaticStateIdentifierManager staticStateIdentifierManager;  

            @Autowired 

            StaticStateManager<StaticStatePersistedOnFileSystemEntity> staticStateManager;  

            @Autowired 

            FileUploaderHelper fileUploaderHelper;  

            @Autowired 

            Authorizer authorizer;  

            /**

            * Maximum time that a streaming request can take.<br>

            */ 

            private long taskTimeOut = DateUtils.MILLIS_PER_HOUR;  

            @Override 

            public void handleRequest(final HttpServletRequest request, final HttpServletResponse response) 

                    throws ServletException, IOException {  

                // process the request 

                try {  

                    //check if uploads are allowed 

                    if (!uploadServletAsyncProcessor.isEnabled()) { 

                        throw new UploadIsCurrentlyDisabled(); 

                    }  

                    // extract stuff from request 

                    final FileUploadConfiguration process = fileUploaderHelper.extractFileUploadConfiguration(request);  

                    log.debug("received upload request with config: "+process);  

                    // verify authorization 

                    final UUID clientId = staticStateIdentifierManager.getIdentifier(); 

                    authorizer.getAuthorization(request, UploadServletAction.upload, clientId, process.getFileId());  

                    //check if that file is not paused 

                    if (uploadServletAsyncProcessor.isFilePaused(process.getFileId())) { 

                        log.debug("file "+process.getFileId()+" is paused, ignoring async request."); 

                        return; 

                    }  

                    // get the model 

                    StaticFileState fileState = staticStateManager.getEntityIfPresent().getFileStates().get(process.getFileId()); 

                    if (fileState == null) { 

                        throw new FileNotFoundException("File with id " + process.getFileId() + " not found"); 

                    }  

                    // process the request asynchronously 

                    final AsyncContext asyncContext = request.startAsync(); 

                    asyncContext.setTimeout(taskTimeOut);  

                    // add a listener to clear bucket and close inputstream when process is complete or 

                    // with 

                    // error 

                    asyncContext.addListener(new UploadServletAsyncListenerAdapter(process.getFileId()) {  

                        @Override 

                        void clean() { 

                            log.debug("request " + request + " completed."); 

                            // we do not need to clear the inputstream here. 

                            // and tell processor to clean its shit! 

                            uploadServletAsyncProcessor.clean(clientId, process.getFileId()); 

                        } 

                    });  

                    // then process 

                    uploadServletAsyncProcessor.process(fileState, process.getFileId(), process.getCrc(), process.getInputStream(), 

                            new WriteChunkCompletionListener() {  

                                @Override 

                                public void success() { 

                                    asyncContext.complete(); 

                                }  

                                @Override 

                                public void error(Exception exception) { 

                                    // handles a stream ended unexpectedly , it just means the user has 

                                    // stopped the 

                                    // stream 

                                    if (exception.getMessage() != null) { 

                                        if (exception.getMessage().equals("Stream ended unexpectedly")) { 

                                            log.warn("User has stopped streaming for file " + process.getFileId()); 

                                        } 

                                        else if (exception.getMessage().equals("User cancellation")) { 

                                            log.warn("User has cancelled streaming for file id " + process.getFileId()); 

                                            // do nothing 

                                        } 

                                        else { 

                                            exceptionCodeMappingHelper.processException(exception, response); 

                                        } 

                                    } 

                                    else { 

                                        exceptionCodeMappingHelper.processException(exception, response); 

                                    }  

                                    asyncContext.complete(); 

                                }  

                            }); 

                } 

                catch (Exception e) { 

                    exceptionCodeMappingHelper.processException(e, response); 

                }  

            }  

        } 

    3,请求流程图:

    主要思路就是将文件切分,然后分块上传。


    参考文献:

    https://blog.csdn.net/moonpure/article/details/54861212

    相关文章

      网友评论

        本文标题:Java http大文件断点续传上传

        本文链接:https://www.haomeiwen.com/subject/awbtgqtx.html