结合服务端实现断点续传

本人第一次写博客,才疏学浅,希望大牛能指出不足,如有错,请勿喷


什么是断点续传?
    断点续传是指,支持从文件上次中断的地方开始传送数据,而并非是从文件开头传送。这样做的优点是,如果在传输一个比较大的文件,发生连接超时错误时,只要客户端记录下已经接受的文件的大小,下次连接时,告诉服务器上次接受了多少,服务器就能跳过你已经接受部分继续传输,而你客户端只要将其追加在上次保存文件位置之后,就能实现断点续传,而不用从头传输,从而减少提高下载效率。并且当我们需要开发下载暂停,暂停后继续下载的功能时,也可以通过断点续传功能实现。
首先我们先了解一下文件在网络中怎么样传输的
    比如我们要传输一个大文件,先在服务端将其转化成字节流写入,然后一字节一字节经过网络进行传输,到客户端取出该字节流,将其接受,保存成相应的文件类型,记得文件类型即MIME类型,也就是文件后缀要一致,因为电脑会根据文件的MIME类型选择正确方式打开,如果不一致可能会无法打开。当然实际中的传输不是这么简单,因为在传输中还要根据相应的网络协议进行包装,这个我们就不深入研究。
怎么实现断点续传?
    我打个将简单的比方:
比如现在有ABCDEFGH八个字母,我要对着再抄一份(这里比喻 传输过程),我就看一眼A,在新纸上写下A,当写到D的时候笔没水了,这时我只要记下我已经写4个字母,下次继续写的时候,我就跳过4个字母,从E继续抄写,并且是写在上次的D后面,当我抄完剩下EFGH四个字母时,我们就完成了抄写工作。
代码实现:
    当我们要下载一个文件时,我们首先要请求网络获取该文件的相关信息,比如文件名,文件大小,文件下载地址,然后将其保存在一个实体类中,在这个实体类中还可以加入该文件已经下载的大小,下载状态之类的信息,根据开发的需要添加;
服务端代码如下:
<%@page import="java.io.File"%>
<%@ page contentType="text/html; charset=utf-8" language="java"
	errorPage=""%>
<%
	String size;
	String name;
	File file = new File("E:/ecilpse_preject/JSp/WebContent/app/Kugou.apk");
	if (file.isFile()) {
		long size1 = file.length();
		size = size1 + "";
		name = file.getName();
	} else {
		size = "-1";
		name = "";
	}

	String s = "{\"name\":\"" + name + "\",\"size\":\"" + size + "\",\"url\":\"JSp/Download\"}";

	out.print(s);
%>
安卓客户端代码如下:
保存信息的实体类:
/**
 * name : 软件名称
 * url: 软件地址
 * size :软件已下载大小
 * maxsize:软件大小
 */
data class app(var name: String, var url: String, var size: Long, var maxsize: Long) : Serializable
安卓端的请求,由于我做了一些封装,故我贴出部分代码供参考:
 Call.getRetrofit().create(DLretrofit::class.java)
                .getapp().enqueue(object : Callback<appbean> {
            override fun onResponse(call: retrofit2.Call<appbean>?, response: Response<appbean>?) {
                Log.v(TAG, response?.toString())
                if (response != null) {
             //用实体类保存要下载文件信息
                    appx = app(name = response.body()?.name!!, url = response.body()?.url!!, size = 0,
                            maxsize = DLUitl.StringToLong(response.body()?.size!!))
             //以下两步作用为初始化界面的控件显示
                    EventBus.getDefault().post(setView(response.body()?.name ?: "", response.body()?.size ?: ""))
                    EventBus.getDefault().post(showButton())
                }

            }

            override fun onFailure(call: retrofit2.Call<appbean>?, t: Throwable?) {
                EventBus.getDefault().post(ToastShow())//显示错误提示!!
                Log.d("123", t.toString())
            }
        })
当我们获得文件信息后,就可以开始我们的文件下载,这里我们会讲到实现断点续传的关键点 RandomAccessFile类

这类在官方文档说明中是这样的:

    这个类的实例支持读取和写入随机访问文件。随机访问文件的行为与存储在文件系统中的大量字节相同。有一种游标,或索引到隐含的数组中,称为文件指针;输入操作从文件指针开始读取字节,并使文件指针超过读取的字节。如果随机访问文件是以读/写模式创建的,那么输出操作也是可用的;输出操作从文件指针开始写入字节,并使文件指针超过写入的字节。写入隐含数组的当前末尾的输出操作会导致数组被扩展。文件指针可以通过getFilePointer方法读取,并通过seek方法设置。

    通过这类我们就可以随意的从任意位置开始读取大文件,这样当客户端需要下载文件,并需要从多少字节开始传输,这时我们只要操作服务端跳过文件多少字节开始传输即可实现断点续传。

服务端代码如下:
public class Download extends HttpServlet {
	private static final long serialVersionUID = 1L;

	public Download() {
		super();
	}

	@SuppressWarnings("resource")
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String size = request.getParameter("size");
		int size_int = Integer.parseInt(size);
		System.out.println(size);
		File file = new File("E:/ecilpse_preject/JSp/WebContent/app/Kugou.apk");
		RandomAccessFile raf = new RandomAccessFile(file, "rw");
		ServletOutputStream outputStream = response.getOutputStream();
		byte[] bs = new byte[2048];
		raf.seek(size_int);
		int len;
		while ((len = raf.read(bs)) > 0) {
			outputStream.write(bs, 0, len);
		}

	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}

}
安卓客户端部分代码:
//下载地址+已经下载文件的大小
            val url: String = Constant.url + app.url + "?size=" + app.size.toString()
            Log.v(TAG, url)
//保存地址
            val fileUrl = Constant.fileUrl + app.name
            Log.v(TAG, fileUrl)

 val mConnectionUrl = URL(url)
            val openConnection = mConnectionUrl.openConnection()//打开连接
            openConnection.connectTimeout = 10000//设置连接超时时间
            openConnection.readTimeout = 10000//设置读取超时时间
            inputStream = openConnection.getInputStream()//打开字节流
            fileOutputStream = FileOutputStream(fileUrl, true)//打开本地保存的文件字节流,并从允许追加保存(关键代码)
            bufferedOutputStream = BufferedOutputStream(fileOutputStream)//包装成缓存字节流

            mCurrentTime = System.currentTimeMillis()//记录开始的时间
            val byte: ByteArray = kotlin.ByteArray(2048)
            if (inputStream != null || fileOutputStream != null || bufferedOutputStream != null) {

                len = inputStream!!.read(byte)
                while (idex > 0) {
                    if (!boolean) {//用于停止和暂停功能的实现(如不想实现可以注释掉)
                        break
                    }
                    bufferedOutputStream!!.write(byte, 0, len)
                    app.size = app.size + idex
                    mDownloadSize += len
//                    以下代码实现功能,通过回调,告诉主线程多少时间下载了多少,实现进度条,和下载速度的显示更新(如不想实现可以注释掉)
                    times++
                    if (times > 10) {
                        mNewTime = System.currentTimeMillis()

                        EventBus.getDefault().post(DLCallback(app.size, mNewTime - mCurrentTime, mDownloadSize))
                        mCurrentTime = mNewTime
                        times = 0
                        mDownloadSize = 0
                    }
                    len = inputStream!!.read(byte)
                }
            }
这样就完整的实现断点续传下载文件的整个流程了。

注:安卓端的代码我用的是Kotlin进行编写,有java基础应该大概可以看懂,如想了解相关语法的建议到https://www.kotlincn.net/docs/reference/进行学习,工作中一般用java开发,但我个人比较喜欢用Kotlin进行开发,个人感觉Kotlin代码相当精简,而且在检查是否非空方面很突出

最后谢谢各位的指教!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: C语言实现FTP断点续传是一项比较复杂的任务,需要对FTP协议有深刻的理解和对C语言编程的熟练掌握。 首先,需要在客户端实现文件的上传和下载功能,并保留上传或下载过程中的文件偏移量,以便进行断点续传。在上传或下载过程中,如果出现中断或错误,可以通过记录文件偏移量和已上传或已下载的字节数来恢复续传。 其次,需要在FTP服务器实现断点续传的支持。FTP服务器需要提供REST命令,该命令用于设置文件传输的起始偏移量。当客户端发起断点续传的请求时,服务端需要根据请求中记录的文件偏移量和已上传或下载的字节数,调用REST命令设置传输起始偏移量,以便从上一次中断的位置继续传输文件。 最后,需要在客户端服务器端加入对传输进度的监控和日志记录,以便在发生错误或中断时能够更方便地找到原因,并进行相应的处理。 总的来说,FTP断点续传实现需要在客户端服务器端都进行相应的编码,需要对FTP协议及其相关命令有足够的理解,并需要细致地处理各种异常情况,才能保证传输的准确性和可靠性。 ### 回答2: FTP (File Transfer Protocol) 是一种用于在计算机网络间传输文件的标准协议,而断点续传则是指在传输中断后恢复传输时只需要传输未完成的部分,而不必重新传输整个文件。在 C 语言中实现 FTP 断点续传需要以下步骤: 1. 连接 FTP 服务器:使用 socket 函数创建一个连接到 FTP 服务器的 socket,然后使用 connect 函数连接到 FTP 服务器的 IP 地址和端口号。 2. 登录 FTP 服务器:使用 send 函数发送用户和密码信息到 FTP 服务器,以实现登录到 FTP 服务器。 3. 选择目录和文件:使用 send 函数发送要上传或下载的文件名和文件路径,以实现选择目录和文件。 4. 初始化传输:使用 send 函数发送 REST(Resume Transfer)命令和文件偏移量信息,以初始化传输。 5. 开始传输:使用 send 和 recv 函数进行文件传输,并在过程中记录已传输的字节数和剩余的字节数,以支持断点续传。 6. 传输结束:使用 send 函数发送 QUIT 命令以退出 FTP 服务器。 以上是使用 C 语言实现 FTP 断点续传的基本步骤,其中还需要考虑一些细节问题,如错误处理、超时设置、文件传输模式等。同时,为了实现更高效的 FTP 断点续传,还需要使用多线程或者异步 I/O 等技术,以提高传输速度和可靠性。 ### 回答3: FTP是文件传输协议,而断点续传指的是在文件传输过程中,若传输中断,可以不需要重新传输整个文件,而是从中断的地方继续传输,以减少传输时间和流量的浪费。在C语言中,实现FTP断点续传的方法如下: 1. 建立FTP连接 使用socket库中的函数建立客户端与FTP服务器的连接,例如使用socket函数创建一个套接字,然后使用connect函数连接到FTP服务器。 2. 读取文件指针位置 在传输时可以使用ftell函数获取当前文件指针位置,用于记录上一次传输的位置。 3. 断点传输 若传输过程中出现中断,可以通过记录的上一次文件指针位置开始传输,使用fseek函数将文件指针移动到上一次读取的位置,接着继续使用send或recv函数进行文件传输。 4. 传输完成 当文件传输完成时,关闭FTP连接,释放套接字。 需要注意的是,FTP断点续传需要服务器端支持断点续传,即在FTP服务器端需要开启断点续传的相关支持,否则客户端进行断点续传也无法成功。而且,在进行断点续传的过程中,需要保证文件的一致性和完整性,防止数据被篡改或重复传输,因此还需要进行文件校验和验证。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值