美文网首页文件系统相关
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