大文件断点续传

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

最近项目较为清闲,研究了下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", name, ByteSource.Util.bytes(size
				+ type + lastModified), 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, 0, 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!

目录
相关文章
|
2月前
|
Java C#
断点续传(上传)C#版
断点续传(上传)C#版
32 0
|
5月前
|
前端开发 NoSQL Redis
如何实现大文件上传:秒传、断点续传、分片上传
如何实现大文件上传:秒传、断点续传、分片上传
454 0
|
5月前
大文件上传如何断点续传
该文档描述了一个大文件上传流程,包括:1) 文件分片,2) 计算文件及分片的Hash值以生成唯一标识符,3) 上传分片并检查已上传状态以避免重复,4) 在上传中断时能恢复,5) 服务端合并分片成原始文件,6) 错误处理(如网络中断、服务器故障、上传失败等)并通知用户,最后7) 返回上传成功信息。
下载文件 | 下载流文件的处理方式
当下载文件时,返回为流的形式,所以在请求的时候设置 responseType:’arraybuffer’ 或者设置为 responseType: ‘blob’ 的时候,只有当接口返回正确的时候才会下载文件,返回错误的时候,应该是提示用户出错了,但是使用 arraybuffer 或者 blob 的时候,错误的信息也是返回这个形式的。所以需要转换一下。
157 0
|
存储 前端开发 NoSQL
注册java实现文件分片上传并且断点续传
一、简单的分片上传 针对第一个问题,如果文件过大,上传到一半断开了,若重新开始上传的话,会很消耗时间,并且你也并不知道距离上次断开时,已经上传到哪一部分了。因此我们应该先对大文件进行分片处理,防止上面提到的问题。
|
存储 前端开发 NoSQL
java实现文件分片上传并且断点续传
针对第一个问题,如果文件过大,上传到一半断开了,若重新开始上传的话,会很消耗时间,并且你也并不知道距离上次断开时,已经上传到哪一部分了。因此我们应该先对大文件进行分片处理,防止上面提到的问题。
356 0
|
前端开发 网络协议 API
大文件处理(上传,下载)思考
在计算文件hash的方式,主要有以下几种: 分片全量计算hash、抽样计算hash。在这两种方式上,分别又可以使用web-work和浏览器空闲(requestIdleCallback)来实现.
大文件处理(上传,下载)思考
|
前端开发 JavaScript Dubbo
大文件上传:秒传、断点续传、分片上传
大文件上传:秒传、断点续传、分片上传
大文件上传:秒传、断点续传、分片上传
|
网络协议 Java
文件断点续传原理与实现
在网络状况不好的情况下,对于文件的传输,我们希望能够支持可以每次传部分数据。首先从文件传输协议FTP和TFTP开始分析, FTP是基于TCP的,一般情况下建立两个连接,一个负责指令,一个负责数据;而TFTP是基于UDP的,由于UDP传输是不可靠的,虽然传输速度很快,但对于普通的文件像PDF这种,少了一个字节都不行。
2692 0
|
缓存 C# 图形学
C#多线程下载、断点续传的实现
做Unity热更功能的时候,发现单线程下载大尺寸资源文件的效率太低,专门去研究了下多线程下载,这里记录下相关知识点。
1049 0