java http大文件上传,断点续传项目研究,Github上传源代码

简介: 1,项目调研 因为需要研究下断点上传的问题。找了很久终于找到一个比较好的项目。 在GoogleCode上面,代码弄下来超级不方便,还是配置hosts才好,把代码重新上传到了github上面。 https://github.com/freewebsys/java-large-file-uploader-demo 效果: 上传中,显示进度,时间,百分比。 点击【Paus
+关注继续查看


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,请求流程图:


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





目录
相关文章
|
6天前
|
设计模式 监控 Java
BT!GitHub开源阿里Java性能调优百宝书仅3小时,标星竟超过30k
我们在日常生活中,并不是碰见的每一个程序都需要进行调优的。如果你做出来的程序的性能表现的和预期一样甚至超越,那就完全没有必要再付出额外的精力去提升它的性能。
|
6天前
|
消息中间件 Java 程序员
GitHub和 Gitee联合编写最新版20w字Java全栈面试手册,简直无敌!
最近小编发现了一份牛逼的Java全栈面试手册,这份面试手册深入到面试官和面试者的角度还原了真实的面试场景对话! 而且还是程序员两大面试巨头平台GitHub和 Gitee联手编写的,其内容可以说是在全网所有面试题中都“首屈一指”内容非常详细很多细节都给大家做了图和怎么应对面试官的问题!
GitHub和 Gitee联合编写最新版20w字Java全栈面试手册,简直无敌!
|
6天前
|
分布式计算 Java 开发者
GitHub爆款!Java性能优化:轻松道破软件性能调优,不止搞定JVM
今天给大家带来的是:周明耀老师的 《大话Java性能优化:轻松道破软件性能调优方法论和具体实现路径》,全面细致,一本书搞定性能优化 周明耀是谁? 12年投资银行项目、分布式计算项目工作经验,IBM开发者论坛专家作者。一名IT技术狂热爱好者,一名顽强到底的工程师。推崇技术创新、思维创新,对于新技术非常的热爱,致力于技术研发、研究,通过发布文章、书籍、互动活动的形式积极推广软件技术。欢迎添加作者“michael_tec”,共同探讨IT技术话题。
|
6天前
|
Dubbo NoSQL Java
GitHub置顶46k星的互联网大厂Java工程师进阶题,腾讯官方首秀!
大家好,最近有不少小伙伴在后台留言,又得准备面试了,不知道从何下手! 可以肯定的是,不管我们要学习提升还是涨薪跳槽!都要先给自己设立一个小目标,然后想着自己的目标努力奋斗就是了。
|
9天前
|
缓存 Java 程序员
面试进阶齐飞!Github一天万赞的阿里Java系统性能优化有多牛?
前两天在知乎上看到一个问答,说的是: 一个Java程序员具备什么样的素质和能力才可以称得上高级工程师? 这个问题也引发了我的一些思考,可能很多人会说,“作为高级工程师,基础得过硬、得熟练掌握一门编程语言、至少看过一个优秀开源项目的源代码、有过高并发/性能优化的工作经验、沟通能力强等等”。
23 0
|
9天前
|
开发框架 前端开发 Java
GitHub首次开源标星20k+项目:Guns-现代化主流Java应用开发框架
Guns是一个现代化的Java应用开发框架,基于主流技术Spring Boot2 + Vue3,Guns的核心理念是提高开发人员开发效率,降低企业信息化系统的开发成本
|
10天前
|
小程序 前端开发 Java
GitHub私活利器【开源版】前后端分离的Java 商城系统(已上线)
Smart Shop 是一款基于 Spring Cloud +MybatisPlus+XXL-JOB+redis+Vue 的前后端分离、分布式、微服务架构的 Java 商城系统
|
16天前
|
算法 Java 数据库
弯道超车!GitHub顶级“Java面试总汇2023”大厂面试一一攻克
前言 目前的java开发市场可谓是异常火热,无论大小公司java岗位都是供不应求。但没有找到满意工作或还在面试中的小伙伴还有很多。
28 0
|
17天前
|
存储 NoSQL Java
面试造火箭?GitHub飙升“2023(Java 岗)面试真题汇总”转载40万
前言 在it行业迅速发展的现在,技术越来越高深复杂。随之而来的就是面试的难度跟知识的存储了,要背越来越多的八股文了问的越来越细了,越来越广泛,和越来越底层了。现在的面试摆明了就是让我们“面试造飞机,入职拧螺丝”。
|
17天前
|
消息中间件 Dubbo Java
裸辞底气!GitHub飙升“java面试笔记2023” 了解下八股文天花板
前言 现在不管是校招还是社招都避免不了面试,而我们程序员面试又避免不了八股文,得疯狂的去背。但很多朋友都背的很盲目资料也不够好。
30 0
推荐文章
更多