最近项目较为清闲,研究了下Html5新增的几个重要技术点,如操作文件系统、获取摄像头麦克风、本地图片预览、Ajax上传文件等。灵光一现,我有了一个“墨迹”的想法:通过浏览器获取影音数据并实时存储到本地磁盘上,再接着后台默默上传该文件,达到视频录像伪实时备份的目的。然而经过两三天的琢磨研究,有一问题怎么也未能解决:从UserMedia中如何获得视音频流?(目前能力有限,先留着以后继续研究)。哎,功亏一篑啊。

不过,一事还是值得记录和分享的,那就是大文件断点续传!总体思路:用js获取文件的相关属性如name、size、type、lastModified等,发送给后台用于确定文件唯一性(我用的是它们4个的MD5散列值,最好是JS给出整个文件的MD5值);上传之前先check,看看已经上传多少字节了;后续上传时只上传未曾上传过的字节。废话不多说,上代码!注释很清晰哦!

界面效果


HTML代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<Meta charset="utf-8">
<Meta name="viewport" content="width=device-width,initial-scale=1">
<title>大文件断点续传</title>
<link rel="stylesheet" href="../public/css/bootstrap.css">
</head>
<body>
	<div class="container-fluid">
		<input type="file" id="file" name="file">
		<progress id="progress"></progress>
		<button onclick="upload()">上传</button>
		<button onclick="abort()">暂停</button>
	</div>
</body>
<script>
	var xhr = new XMLHttpRequest();//上传请求
	xhr.onreadystatechange = function() {//上传状态监听
		console.log(xhr.readyState + "-" + xhr.status + "-" + xhr.responseText);
		if (xhr.readyState == 4 && xhr.status == 200) {
			alert("上传成功");
		}
	};
	var proEle = document.getElementById("progress");
	var loaded = 0;//记录服务器上已上传成功的字节数
	xhr.upload.onprogress = function(e) {//上传进度监听
		proEle.value = loaded + e.loaded;
	};
	
	//暂停方法
	function abort() {
		xhr.abort();
	}
	
	/**上传方法。
	 * 上传之前先check,查询已上传字节数。
	 * 若字节数为“0”表示文件未上传过,若字节数等于待上传文件大小表示文件曾经上传成功过,无需再次上传。
	 */
	function upload() {
		var fileEle = document.getElementById("file");
		var file = fileEle.files[0];
		var param = "name=" + file.name + "&size=" + file.size + "&type="
				+ file.type + "&lastModified=" + file.lastModified;//后台通过name、size、type、lastModified四个属性来确定文件唯一性。
		/**
		 * 进度条初始化
		 */
		proEle.value = 0;
		proEle.max = file.size;

		var xhrCheck = new XMLHttpRequest();//检查请求
		xhrCheck.onreadystatechange = function() {//检查状态监听,成功后执行发送上传请求
			if (xhrCheck.status == 200 && xhrCheck.readyState == 4) {
				loaded = parseInt(xhrCheck.responseText);
				proEle.value = loaded;

				if (loaded < file.size) {
					xhr.open("POST","/web/api/device/upload?" + param,true);
					xhr.send(file.slice(loaded,file.size,file.type));//通过File.slice方法获得未上传过的数据信息,再上传(重点)
				} else {
					alert('文件已存在');
				}
			}
		};
		xhrCheck.open("GET","/web/api/device/check?" + param,true);
		xhrCheck.send();//发送检查请求
	}
</script>
</html>

Java代码

@RequestMapping(value = "/check",method = RequestMethod.GET)
	@ResponseBody
	/**
	 * 上传检查。
	 * 上传之前先check,查询已上传字节数。文件不存在就返回0
	 * @param request
	 * @return
	 */
	public String check(HttpServletRequest request) {
		String name = request.getParameter("name");
		String size = request.getParameter("size");
		String type = request.getParameter("type");
		String lastModified = request.getParameter("lastModified");
		String fileID = new SimpleHash("MD5",name,ByteSource.Util.bytes(size
				+ type + lastModified),2).toHex();// 通过name、size、type、lastModified四个属性来确定文件唯一性——MD5散列值。
		String fileName = fileID + "-" + name;
		File file = new File("D://" + fileName);
		if (!file.exists()) {
			return "0";
		} else {
			return file.length() + "";
		}
	}

	@RequestMapping(value = "/upload",method = RequestMethod.POST)
	@ResponseBody
	/**
	 * 上传方法
	 * 主体就是将InputStream流中的数据写入目标文件中,注意是append
	 * @param request
	 * @return
	 */
	public String upload(HttpServletRequest request) {
		String name = request.getParameter("name");
		String size = request.getParameter("size");
		String type = request.getParameter("type");
		String lastModified = request.getParameter("lastModified");
		String fileID = new SimpleHash("MD5",2).toHex();// 通过name、size、type、lastModified四个属性来确定文件唯一性——MD5散列值。
		String fileName = fileID + "-" + name;
		ServletInputStream is = null;
		FileOutputStream os = null;
		File file = null;
		try {
			is = request.getInputStream();
			BufferedInputStream bis = new BufferedInputStream(is);
			byte[] b = new byte[1024 * 1024];
			file = new File("D://" + fileName);
			if (!file.exists()) {
				file.createNewFile();
			}
			os = new FileOutputStream(file,true);// append
			int n = 0;
			while ((n = bis.read(b)) > 0) {
				os.write(b,n);
			}
		} catch (IOException e) {
			e.printstacktrace();
		} finally {
			try {
				os.close();
				is.close();
			} catch (IOException e) {
				e.printstacktrace();
			}
		}
		return "SUCCESS";
	}

写在最后

断点续传老的做法是将大文件分割成N个的小文件再已Form表单形式上传,进而后台进行类似文件拼接的相关操作。效率还可以,但颗粒度就没有上面新的方法细了,哈哈。上面的新方法颗粒度直接精确到字节了。科技在发展,社会在进步,码农也能少费点脑子敲代码,留着打DOTA了!

Best Wishes For You!

大文件断点续传的更多相关文章

  1. PHP 断点续传实例详解

    这篇文章主要介绍了PHP 断点续传的相关知识,非常不错,具有参考借鉴价值,需要的朋友可以参考下

  2. Java实现断点续传功能的示例代码

    这篇文章主要为大家详细介绍了如何利用Java语言实现网络资源的断点续传功能,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的可以了解一下

  3. Java实现多线程下载和断点续传

    这篇文章主要为大家详细介绍了Java实现多线程下载和断点续传,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  4. iOS11 下载之断点续传的bug的解决方法

    本篇文章主要介绍了iOS11 下载之断点续传的bug的解决方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  5. java实现文件的断点续传

    这篇文章主要为大家详细介绍了java实现文件的断点续传,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  6. iOS开发网络编程之断点续传

    在下载较大的文件的时候,一次不能下载完毕,这就需要用到断点续传,那么在IOS开发中该如何实现呢,下面跟着小编一起通过本文来学习下。

  7. js自己实现一个大文件切片上传+断点续传的示例代码

    本文主要介绍了js自己实现一个大文件切片上传+断点续传的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  8. RecyclerView实现侧滑和网络断点续传

    这篇文章主要为大家详细介绍了RecyclerView实现侧滑和网络断点续传,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. iOS开发网络篇—实现大文件的多线程断点下载

    iOS开发中经常会用到文件的下载功能,这篇文章主要介绍了iOS开发网络篇—实现大文件的多线程断点下载,今天咱们来分享一下思路。

  10. 总结iOS开发中的断点续传与实践

    本文先从断点续传问题开始,介绍断点续传概述和原理。接着结合笔者调研中尝试的 AFHTTPRequestOpeartion,简单分析源码。最后分别基于 NSURLConnection,NSURLSessionDataTask 和 NSURLSessionDownloadTask 去实现应用重启情况下的断点续传。下面一起来看看。

随机推荐

  1. xe-ajax-mock 前端虚拟服务

    最新版本见Github,点击查看历史版本基于XEAjax扩展的Mock虚拟服务插件;对于前后端分离的开发模式,ajax+mock使前端不再依赖后端接口开发效率更高。CDN使用script方式安装,XEAjaxMock会定义为全局变量生产环境请使用xe-ajax-mock.min.js,更小的压缩版本,可以带来更快的速度体验。

  2. vue 使用 xe-ajax

    安装完成后自动挂载在vue实例this.$ajaxCDN安装使用script方式安装,VXEAjax会定义为全局变量生产环境请使用vxe-ajax.min.js,更小的压缩版本,可以带来更快的速度体验。cdnjs获取最新版本点击浏览已发布的所有npm包源码unpkg获取最新版本点击浏览已发布的所有npm包源码AMD安装require.js安装示例ES6Module安装通过Vue.use()来全局安装示例./Home.vue

  3. AJAX POST数据中文乱码解决

    前端使用encodeURI进行编码后台java.net.URLDecoder进行解码编解码工具

  4. Koa2框架利用CORS完成跨域ajax请求

    实现跨域ajax请求的方式有很多,其中一个是利用CORS,而这个方法关键是在服务器端进行配置。本文仅对能够完成正常跨域ajax响应的,最基本的配置进行说明。这样OPTIONS请求就能够通过了。至此为止,相当于仅仅完成了预检,还没发送真正的请求呢。

  5. form提交时,ajax上传文件并更新到&lt;input&gt;中的value字段

  6. ajax的cache作用

    filePath="+escape;},error:{alert;}});解决方案:1.加cache:false2.url加随机数正常代码:网上高人解读:cache的作用就是第一次请求完毕之后,如果再次去请求,可以直接从缓存里面读取而不是再到服务器端读取。

  7. 浅谈ajax上传文件属性contentType = false

    默认值为contentType="application/x-www-form-urlencoded".在默认情况下,内容编码类型满足大多数情况。在这里,我们主要谈谈contentType=false.在使用ajax上传文件时:在其中先封装了一个formData对象,然后使用post方法将文件传给服务器。说到这,我们发现在JQueryajax()方法中我们使contentType=false,这不是冲突了吗?这就是因为当我们在form标签中设置了enctype=“multipart/form-data”,

  8. 909422229_ajaxFileUpload上传文件

    ajaxFileUpload.js很多同名的,因为做出来一个很容易。我上github搜AjaxFileUpload出来很多类似js。ajaxFileUpload是一个异步上传文件的jQuery插件传一个不知道什么版本的上来,以后不用到处找了。语法:$.ajaxFileUploadoptions参数说明:1、url上传处理程序地址。2,fileElementId需要上传的文件域的ID,即的ID。3,secureuri是否启用安全提交,默认为false。4,dataType服务器返回的数据类型。6,error

  9. AJAX-Cache:一款好用的Ajax缓存插件

    原文链接AJAX-Cache是什么Ajax是前端开发必不可少的数据获取手段,在频繁的异步请求业务中,我们往往需要利用“缓存”提升界面响应速度,减少网络资源占用。AJAX-Cache是一款jQuery缓存插件,可以为$.ajax()方法扩展缓存功能。

  10. jsf – Ajax update/render在已渲染属性的组件上不起作用

    我试图ajax更新一个有条件渲染的组件。我可以确保#{user}实际上是可用的。这是怎么引起的,我该如何解决呢?必须始终在ajax可以重新呈现之前呈现组件。Ajax正在使用JavaScriptdocument.getElementById()来查找需要更新的组件。但是如果JSF没有将组件放在第一位,那么JavaScript找不到要更新的内容。解决方案是简单地引用总是渲染的父组件。

返回
顶部