Java服务器端支持断点续传代码

private void downLoad(String filePath, HttpServletRequest request, HttpServletResponse response) {
		File downloadFile = new File(filePath);//要下载的文件
		long fileLength = downloadFile.length();//记录文件大小
		long pastLength = 0;//记录已下载文件大小
		int rangeSwitch = 0;//0:从头开始的全文下载;1:从某字节开始的下载(bytes=27000-);2:从某字节开始到某字节结束的下载(bytes=27000-39000)
		long toLength = 0;//记录客户端需要下载的字节段的最后一个字节偏移量(比如bytes=27000-39000,则这个值是为39000)
		long contentLength = 0;//客户端请求的字节总量
		String rangeBytes = "";//记录客户端传来的形如“bytes=27000-”或者“bytes=27000-39000”的内容 
		RandomAccessFile raf = null;//负责读取数据
		OutputStream os = null;//写出数据
		OutputStream out = null;//缓冲
		byte b[] = new byte[1024];//暂存容器
		String fileName = downloadFile.getName();
		try {
			if (filePath.lastIndexOf("/") > 0) {
				fileName = new String(filePath.substring(filePath.lastIndexOf("/") + 1, filePath.length()).getBytes(
						"UTF-8"), "ISO8859_1");
			} else if (filePath.lastIndexOf("\\") > 0) {
				fileName = new String(filePath.substring(filePath.lastIndexOf("\\") + 1, filePath.length()).getBytes(
						"UTF-8"), "ISO8859_1");
			}
		} catch (Exception e) {
			log.info(e.getLocalizedMessage());
		}

		if (request.getHeader("Range") != null) {// 客户端请求的下载的文件块的开始字节
			log.info("request.getHeader(\"Range\")=" + request.getHeader("Range"));
			rangeBytes = request.getHeader("Range").replaceAll("bytes=", "");
			if (rangeBytes.indexOf('-') == rangeBytes.length() - 1) {//bytes=969998336-
				rangeSwitch = 1;
				rangeBytes = rangeBytes.substring(0, rangeBytes.indexOf('-'));
				pastLength = Long.parseLong(rangeBytes.trim());
				contentLength = fileLength - pastLength + 1;//客户端请求的是 969998336 之后的字节
			} else {//bytes=1275856879-1275877358
				rangeSwitch = 2;
				String temp0 = rangeBytes.substring(0, rangeBytes.indexOf('-'));
				String temp2 = rangeBytes.substring(rangeBytes.indexOf('-') + 1, rangeBytes.length());
				pastLength = Long.parseLong(temp0.trim());//bytes=1275856879-1275877358,从第 1275856879 个字节开始下载
				toLength = Long.parseLong(temp2);//bytes=1275856879-1275877358,到第 1275877358 个字节结束
				contentLength = toLength - pastLength + 1;//客户端请求的是 1275856879-1275877358  之间的字节
			}
		} else {//从开始进行下载
			contentLength = fileLength;//客户端要求全文下载
		}

		/**
		 * 如果设设置了Content-Length,则客户端会自动进行多线程下载。如果不希望支持多线程,则不要设置这个参数。
		 * 响应的格式是:
		 * Content-Length: [文件的总大小] - [客户端请求的下载的文件块的开始字节]
		 * ServletActionContext.getResponse().setHeader("Content-Length",
		 * new Long(file.length() - p).toString());
		 */
		response.reset();//告诉客户端允许断点续传多线程连接下载,响应的格式是:Accept-Ranges: bytes
		response.setHeader("Accept-Ranges", "bytes");//如果是第一次下,还没有断点续传,状态是默认的 200,无需显式设置;响应的格式是:HTTP/1.1 200 OK
		if (pastLength != 0) {
			//不是从最开始下载,
			//响应的格式是:
			//Content-Range: bytes [文件块的开始字节]-[文件的总大小 - 1]/[文件的总大小]
			log.info("不是从开始进行下载!服务器即将开始断点续传...");
			log.info("下载类型:" + rangeSwitch);
			switch (rangeSwitch) {
			case 1: {//针对 bytes=27000- 的请求
				String contentRange = new StringBuffer("bytes ").append(new Long(pastLength).toString()).append("-")
						.append(new Long(fileLength - 1).toString()).append("/")
						.append(new Long(fileLength).toString()).toString();
				response.setHeader("Content-Range", contentRange);
				response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
				break;
			}
			case 2: {//针对 bytes=27000-39000 的请求
				String contentRange = rangeBytes + "/" + new Long(fileLength).toString();
				response.setHeader("Content-Range", contentRange);
				response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
				break;
			}
			default: {
				break;
			}
			}
		} else {
			//是从开始下载
			log.info("是从开始进行下载!");
		}

		try {
			response.addHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
			response.setContentType("APPLICATION/OCTET-STREAM");// set the MIME type.
			response.addHeader("Content-Length", String.valueOf(contentLength));
			os = response.getOutputStream();
			out = new BufferedOutputStream(os);
			raf = new RandomAccessFile(downloadFile, "r");
			try {
				switch (rangeSwitch) {
				case 0: {//普通下载,或者从头开始的下载
					//同1
				}
				case 1: {//针对 bytes=27000- 的请求
					raf.seek(pastLength);//形如 bytes=969998336- 的客户端请求,跳过 969998336  个字节
					int n = 0;
					while ((n = raf.read(b, 0, 1024)) != -1) {
						out.write(b, 0, n);
					}
					break;
				}
				case 2: {//针对 bytes=27000-39000 的请求
					raf.seek(pastLength - 1);//形如 bytes=1275856879-1275877358 的客户端请求,找到第 1275856879 个字节
					int n = 0;
					long readLength = 0;//记录已读字节数
					while (readLength <= contentLength - 1024) {//大部分字节在这里读取
						n = raf.read(b, 0, 1024);
						readLength += 1024;
						out.write(b, 0, n);
					}
					if (readLength <= contentLength) {//余下的不足 1024 个字节在这里读取
						n = raf.read(b, 0, (int) (contentLength - readLength));
						out.write(b, 0, n);
					}
					break;
				}
				default: {
					break;
				}
				}
				out.flush();
			} catch (IOException ie) {
				/**
				 * 在写数据的时候,
				 * 对于 ClientAbortException 之类的异常,
				 * 是因为客户端取消了下载,而服务器端继续向浏览器写入数据时,
				 * 抛出这个异常,这个是正常的。
				 * 尤其是对于迅雷这种吸血的客户端软件,
				 * 明明已经有一个线程在读取 bytes=1275856879-1275877358,
				 * 如果短时间内没有读取完毕,迅雷会再启第二个、第三个。。。线程来读取相同的字节段,
				 * 直到有一个线程读取完毕,迅雷会 KILL 掉其他正在下载同一字节段的线程,
				 * 强行中止字节读出,造成服务器抛 ClientAbortException。
				 * 所以,我们忽略这种异常
				 */
				//ignore
			}
		} catch (Exception e) {
			log.info(e.getLocalizedMessage());
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					log.info(e.getLocalizedMessage());
				}
			}
			if (raf != null) {
				try {
					raf.close();
				} catch (IOException e) {
					log.info(e.getLocalizedMessage());
				}
			}
		}
	}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java实现Socket断点续传代码可以使用Java的Socket类来实现,并可以使用InputStream和OutputStream类来读取和写入Socket的数据。此外,可以使用RandomAccessFile来实现断点续传功能,具体实现代码如下: ``` InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); RandomAccessFile raf = new RandomAccessFile(fileName, "rw"); // 设置文件偏移量 raf.seek(processedBytes); byte[] buf = new byte[1024]; int len = 0; while ((len = in.read(buf)) != -1) { out.write(buf, 0, len); raf.write(buf, 0, len); processedBytes += len; } raf.close(); in.close(); out.close(); socket.close(); ``` ### 回答2: 要实现Java的socket断点续传功能,可以按照以下步骤进行: 1. 建立服务器端和客户端的连接,并创建输入输出流。服务器端需要记录客户端上传文件的断点位置。 2. 客户端需要读取要上传的文件,然后将文件字节分割成多个小块。可以使用FileInputStream读取文件,然后使用BufferedInputStream增加缓冲区。 3. 客户端将各个小块的文件字节通过socket连接发送给服务器端服务器端接收到数据后,根据客户端传输的块索引和字节信息,将每个小块的文件字节写入到服务器的目标文件中。可以使用FileOutputStream和BufferedOutputStream来写入文件。 4. 客户端在传输过程中如果中断,可以通过记录已传输的字节数或块索引位置等信息,下次继续从该位置开始传输。服务器端则需要根据记录的信息,在继续写入文件前进行校验和处理。 5. 客户端和服务器端之间可以通过一个特定的信号或命令来指示传输是否结束。当传输结束时,客户端可以关闭连接,服务器端也可以完成文件的接收。 需要注意的是,断点续传功能的实现还涉及到传输速度的控制、数据校验、异常处理等问题,具体实现时需要根据实际情况进行调整。同时,在多线程环境下实现断点续传功能可能会更复杂,需要考虑线程同步等问题。 综上所述,以上是一个简单的Java实现socket断点续传功能的步骤。具体的代码实现可以根据需求和实际情况进行调整和扩展。 ### 回答3: Java中实现Socket断点续传代码如下所示: ```java import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class FileServer { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(9999); System.out.println("等待客户端连接..."); Socket socket = serverSocket.accept(); System.out.println("客户端连接成功!"); DataInputStream dis = new DataInputStream(socket.getInputStream()); String fileName = dis.readUTF(); long fileLength = dis.readLong(); File directory = new File("D:/temp"); if (!directory.exists()) { directory.mkdir(); } File file = new File(directory.getAbsolutePath() + File.separatorChar + fileName); FileOutputStream fos = new FileOutputStream(file); byte[] bytes = new byte[1024]; int length; long received = 0; while ((length = dis.read(bytes, 0, bytes.length)) != -1) { fos.write(bytes, 0, length); fos.flush(); received += length; System.out.println("已接收:" + received + "/" + fileLength); } System.out.println("文件接收完成!"); fos.close(); dis.close(); socket.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` ```java import java.io.*; import java.net.Socket; public class FileClient { public static void main(String[] args) { try { Socket socket = new Socket("localhost", 9999); File file = new File("D:/temp/test.txt"); DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); dos.writeUTF(file.getName()); dos.flush(); dos.writeLong(file.length()); dos.flush(); FileInputStream fis = new FileInputStream(file); byte[] bytes = new byte[1024]; int length; long sent = 0; while ((length = fis.read(bytes, 0, bytes.length)) != -1) { dos.write(bytes, 0, length); dos.flush(); sent += length; System.out.println("已发送:" + sent + "/" + file.length()); } System.out.println("文件发送完成!"); dos.close(); fis.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` 上述代码实现了一个简单的Socket断点续传功能。FileServer是服务器端代码,FileClient是客户端的代码服务器端负责监听客户端的连接,接收文件并保存到指定目录。客户端读取指定文件,并将文件发送至服务器端。通过读取和写入字节流的方式实现文件传输。在传输过程中,打印已接收和已发送的字节数,以及文件总大小,以便显示进度信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值