Java http大文件断点续传上传

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271