java 使用RandomAssessFile类多线程切片下载文件之服务端如何实现

上一篇我写了如何使用 java 结合网络连接+多线程+RandomAssessFile类实现多线程切片下载并显示网速https://blog.csdn.net/yali_aini/article/details/81942036

因为之前写的都是客户端,不需要去管服务端,直接把文件放服务器里面,直接访问,服务器(tomcat之类得)就会自动帮我们切片,之类的。然后我自己想测试一些直接访问文件和使用控制器io读写返回文件哪个快一些(肯定是io)https://blog.csdn.net/yali_aini/article/details/81745883,然后测试,发现按照我之前的写法无法做到切片下载,研究了下,解决了这个问题,所以拿出来分享下。

我们都知道,客户端下载的时候设置了 请求头文件,设置了 range 请求范围,服务器端获取范围,返回范围段内的数据就ok了

这里得注意点,因为 rang读取是包头包尾的,如果 1-2 读取的就是 2 个,直接 2-1 的话就是一个,所以这里计算真实读取大小的时候得加个1,不然就会出现 读取少了字节得bug。

代码不怎么难,一起看看把,注意看注释,思路就是我上面说的,获取客户端请求得 range,然后按照范围去读取数据,然后返回就ok了

	@RequestMapping("/downloadFile")
	public void getFile(String name, HttpServletRequest request , HttpServletResponse response) {
		try {
			System.out.println("下载的文件地址:"+ name );
			
			File file = new File(name);
			
			// 文件写出
			BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
			// 获取文件读取
			RandomAccessFile read = new RandomAccessFile(file, "rw");
			
			// 断点分部下载起始点
			long startPos = 0;
			long endPos = -1;
			Enumeration<String> names = request.getHeaderNames();
			while(names.hasMoreElements()) {
				String n = names.nextElement();
				String rangeData = request.getHeader(n);
				System.out.println( n + "---" + request.getHeader(n) );
				if("range".equalsIgnoreCase(n)) {
					if( rangeData !=null && rangeData.indexOf("=")!=-1 ) {
						String [] arr = rangeData.substring(rangeData.indexOf("=")+1).split("-");
						if(arr.length==2) {
							startPos = Integer.valueOf(arr[0]);
							endPos = Integer.valueOf(arr[1]);
							System.out.println( startPos + "---" + endPos );
						}
					}
				}
			}
			
			if(endPos!=-1) {
				read.seek(startPos);
			}else {
				endPos = file.length();
			}
			
			// 设置返回类型
		    response.setContentType("multipart/form-data");
		    // 文件名转码一下,不然会出现中文乱码
		    response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode("--chenparty下载站--" + file.getName(),"UTF-8"));
		    // 设置返回的文件的大小
		    response.setContentLength((int)file.length());
		    
		    byte [] b = new byte[1024];
		    int len = 0;
		    
		    // 获取真实的读取大小,因为 rang读取是包头包尾的,如果 1-2 读取的就是 2 个,直接 2-1 的话就是一个,所以这里得+1
		    long actualLen = endPos - startPos + 1 ;
		    
		    while(-1 != (len = read.read(b))) {
		    	if( actualLen - len >= 0 ) {
		    		out.write(b, 0, len);
		    		// 记录实时的 真实读取大小
		    		actualLen -= len;
		    	}else {
		    		// 真实大小读取完毕
		    		out.write(b, 0, (int)actualLen);
		    		break;
		    	}
		    }
		    
		    read.close();
		    out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

 

阅读更多
换一批

没有更多推荐了,返回首页