精华内容
下载资源
问答
  • 处理断点续传算法

    2020-06-04 11:02:55
    需求分析 《多文件云传输》项目中有这样一个应用背景:...我们把接下来发送未发送的片段这个操作称为断点续传。那么如果我们要进行断点续传,首先,我们要统计出是哪个片段的确实。 接下来这个算法就是用来统计未发送的

    需求分析

    《多文件云传输》项目中有这样一个应用背景:

    文件的发送是按照已定的规则被拆分成好几个片段,每一个片段包括文件的编号和这个片段首字节在整个文件中的偏移和这个片段的长度。提供这三个信息,是为了接受时能根据这些信息,把片段再写回成一整个文件。
    但是文件的发送并不是每次都能够成功,可能某一个发送方在发送时,突然掉线,那么接受时,某个文件可能就会缺少一个片段。我们把接下来发送未发送的片段这个操作称为断点续传。那么如果我们要进行断点续传,首先,我们要统计出是哪个片段的确实。
    接下来这个算法就是用来统计未发送的片段

    举例

    假设一个文件长度为20:下面的数字分别表示未接受的各个字节
    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 写成0:20,0是偏移,20是长度

    第一次接收到片段5:4,把原有片段分成俩个片段: 未接受片段 0:5,9:11
    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

    第二次接收到片段12:4。 未接受片段0:5,9:3,16:4
    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

    第三次接收到片段9:3 未接受片段0:5,16:4
    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

    接收完毕,则未接受的片段就是0:5,16:4。
    那么我们是如何来计算出未接受的片段的?

    1. 假设我们把0:20表示成 curOff :curLen,第一次接收到的表示成recOff:recLen
      即curOff:curLen = 0:20;recOff:recLen = 5:4
      左片段被表示为:curOff:recOff-curOff -> 0:5
      右片段为:recOff + recLen:curOff+curLen-recOff -> 9:11
      计算出这俩个片段之后,顺序的放在一个链表中存储。

    2. 当进行到第二次接收时,发现原有的片段已经有俩个了,那么我们刚才的算法还怎么用呢?
      首先我们要在原记录的片段中找到合适的片段当做当前片段。第二次接收的片段12:4,这个片段在9:11片段范围内,所以我们就把9:11当做当前片段,继续第一部的算法。
      curOff:curLen = 9:11
      recOff:recOff = 12:4
      原来的9:11变成俩个片段:9:3,16:4,把这俩个片段一次放入到链表。
      现在的链表存放:0:5,9:3,16:4

    3. 当第三次时,又出现了了特殊的情况,得到片段9:3,计算得出应该在原来9:3这个范围。
      根据原有的算法得出,左片段9:0,右片段12:0,这俩个片段长度都是0,所以不能被加到链表中去。应该直接剔除。到这里,我们的算法分析完毕。可以考虑写出代码。

    代码

    public void receiveSection(long offset, int len)
                throws ReceiveSectionOutOfRangeException {
            int index = searchSection(offset);
            SectionInfo curSection = sectionList.get(index);
    
            long curOff = curSection.getOffset();
            long curLen = curSection.getLen();
            long recOff = offset;
            long recLen = len;
    
            long lOff = curOff;
            long lLen = recOff - curOff;
            long rOff = recOff + recLen;
            long rLen = curOff + curLen - rOff;
    
            sectionList.remove(index);
            if (rLen > 0) {
                sectionList.add(index, new SectionInfo(rOff, rLen));
            }
            if (lLen > 0) {
                sectionList.add(index, new SectionInfo(lOff, lLen));
            }
        }
    
        private int searchSection(long offset) throws ReceiveSectionOutOfRangeException {
            for (int i = 0; i < sectionList.size(); i++) {
                if(sectionList.get(i).inRange(offset)){
                    return i;
                }
            }
            throw new ReceiveSectionOutOfRangeException("文件编号:"+fileNo+" 偏移量:"+offset);
        }
    

    运用这个代码,会大大减少空间的浪费,降低时间复杂度。如果用数组来标记已经接受的字节,那么就很浪费许多的内存空间。

    展开全文
  • c/c++线程断点续传实现

    热门讨论 2014-03-27 19:15:10
    c/c++线程断点续传实现 cocos2dx断点续传下载 具体可以去我博客中学习 http://blog.csdn.net/vpingchangxin/article/details/22309067
  • C++ 断点续传

    热门讨论 2012-12-21 16:21:38
    服务端和客户端,C++ socket 编程实现,支持断点续传,可供参考学习用。
  • 目前比较常用的断点续传的方法有两种,一种是通过websocket接口进行文件上传,另一种是通过ajax,两种方法各有千秋,虽然websocket听起来比较高端些,但是除了用了不同的协议外其他的算法基本上都是很相似的,并且...
  • http 断点续传 C++ 实现 vs2003

    热门讨论 2009-04-09 14:02:12
    原理: ...2. 获得文件大小ftell, 格式化HTTP请求头 "Range: bytes=ftell -", 以偏移httpfile指针,实现断点续传 3. 获得要下载的文件,以二进制形式传输,OpenURL 4. 接收数据,防止阻塞PeekMessage
  • 针对原有算法无法自动进行断点,即重新开启任务的弊端,在主程序进行改造,一旦发现下载停滞超过5秒,则自动执行断点,重新下载未完成的任务,避免手工执行该操作。
  • 【开发经验】java 20行代码实现断点续传

    千次阅读 多人点赞 2020-07-11 15:11:24
    随着文件的越来越大,很多场景需要断点续传功能,这个功能很友好,大文件上传了一半,下次还能接着上传,免得断点或者关机之后还需要从头开始。 断点上传思路很简单,只需要在上传时将大文件切割为多个分片,将这些...

    引言
    随着文件的越来越大,很多场景需要断点续传功能,这个功能很友好,大文件上传了一半,下次还能接着上传,免得断点或者关机之后还需要从头开始。
    断点上传思路很简单,只需要在上传时将大文件切割为多个分片,将这些小的分片进行上传。
    在这里插入图片描述

    实现思路

    1. 上传文件时判断是否有上传过。
    2. 如果没有上传过,则直接对文件进行切割,切割成很多分片进行上传。
    3. 如果有上传过,则判断之前上传了那些分片,只对于剩下的分片进行上传即可;

    1.如何判断文件是否有上传过?

    ​ 通过散列函数进行文件的标识,来判断文件是否有上传过。散列函数(Hash),一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。附录中可查看散列函数的特性。

    2.如何进行文件的切割?

    ​ 文件切割,这个就是客户端的工作了,对于一个专职后端的开发来说,做文件切割,虽然是有一点点的困难,但是简单的做一做还是必须要可以的。上代码!

      <form enctype="multipart/form-data">
        <input type="file" id="file" value="选择文件"/>
      </form>
    

    ​ 简单来一个文件上传的入口。通过ajax进行上传。

    // 引入jquery
    <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>  
    
    $("#file").change(function () {
                   var file = $("#file").get(0).files[0];
                   // 查询是否有上传过
                   // 这里应该是有一个查询的逻辑的,不在做了哈
                   // 就直接冲第0个分片开始传了。
                   var shardIndex = 0;
                   uploadFile(file,shardIndex);
    
      })
    

    通过file.slice方法进行文件的切割,用来处理开始字节数和结束字节的位置。

     function uploadFile(file,shardIndex) {
         			// 声明 文件信息和分片信息。
         			// 注意:shardTotal是总分片数,即使后面有一点点字节,也要进行加1操作。避免丢了字节。
                    var name = file.name,
                        size = file.size,
                        shardSize = 1024*1024*10,
                        shardTotal = Math.ceil( size /shardSize);
                    if(shardIndex >=shardTotal){
                        return ;
                    }
                    var start = shardIndex*shardSize;
                    var end = Math.min(start+shardSize,size);
         			// 文件切割
                    var packet = file.slice(start,end);
                    var formData = new FormData();
                    formData.append("file",packet);
         //                formData.append("hashCode",md5(file));
                    formData.append("hashCode","1111");  // 这里应该进行hash运算。用111代替一下
                    formData.append("fileName",name);
                    formData.append("size",size);
                    formData.append("shardIndex",shardIndex);
                    formData.append("shardSize",shardSize);
                    formData.append("shardTotal",shardTotal);
         			// 通过ajax进行上传
                    $.ajax({
                        url:"http://localhost:8080/file/upload",
                        type:"post",
                        cache: false,
                        data: formData,
                        processData: false,
                        contentType: false,
                        success :function (data) {
                            console.log(data);
                            //这里进行循环调用,把所有的分片循环上传
                            if(shardIndex<shardTotal){
                                shardIndex++;
                                uploadFile(file,shardIndex);
                            }
                        }
                    });
                }
    

    前后端分离太久了, 就不善于看前端代码,慢慢的更熟悉http请求信息了。请求的信息长这个样子,后来进行接收就好了。认准有二进制信息的提交。如果没有,可能是前端代码有问题了。

    在这里插入图片描述

    3.后端文件接收

    接收文件信息

    public class FileInfo {
        private String hasdCode;
        private String fileName;
        private Integer size;
        private Integer shardIndex;
        private Integer shardSize;
        private Float shardTotal;
        }
    
    @RestController
    public class FileController {
        private static HashMap<String,FileInfo> fileMap = new HashMap();
        // 模拟保存的位置
        private String savePath="E:\\data";
        @PostMapping(value = "/file/upload")
        public void fileUpload(FileInfo fileInfo, HttpServletRequest request) throws IOException {
            MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
            MultipartFile multipartFile = multipartRequest.getFile("file"); // 通过参数名获取指定文件
            //创建分片
            File fileblob = new File(savePath+"\\"+fileInfo.getFileName()+"."+"blob");
            System.out.println("保存分片"+fileblob.getName());
            //保存分片  后面为true 表示在已有的文件进行追加。
            FileCopyUtils.copy(multipartFile.getInputStream(),new FileOutputStream(fileblob,true));
            //判断是否全部上传完成。
            if((fileInfo.getShardIndex()+1) == Math.ceil(fileInfo.getShardTotal())){
                System.out.println("上传完成,重命名");
                File saveFile = new File(savePath+"\\"+fileInfo.getFileName());
                //重命名操作
                fileblob.renameTo(saveFile);
            }
            // 类似保存到数据库,这里简化下,直接放到map中。
            fileMap.put(fileInfo.getHasdCode(),fileInfo);
        }
        @GetMapping("/getfile/{hashCode}")
        public FileInfo getFileInfoByCode(@PathVariable("hashCode") String hashCode){
            return fileMap.get(hashCode);
        }
    }
    

    至此,断点续传的核心逻辑是完成了。但是代码是有很多需要优化。比如数据库如何进行保存数据等等。

    简单的文件复制大家都直到,但是如何复制的更快,却是一个比较难的优化,毕竟java在对系统操作的时候有先天的不足。如果有兴趣的可以看下这个文章,讲述了如何更好的进行文件IO的处理。不过对于一般开发的应用程序来说,既然都用到java来操作文件了,应该也不太在乎复制效率了,这个道理貌似和”吃泡面的时候还在乎什么健康“一样。

    笔者环境

    spring boot 2.3.1.RELEASE JDK1.8

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.test</groupId>
        <artifactId>file-upload-test</artifactId>
        <version>1.0-SNAPSHOT</version>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.1.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

    附录

    散列函数

    ​ 所有散列函数都有如下一个基本特性:如果两个散列值是不相同的(根据同一函数),那么这两个散列值的原始输入也是不相同的。这个特性是散列函数具有确定性的结果。但另一方面,散列函数的输入和输出不是一一对应的,如果两个散列值相同,两个输入值很可能是相同的,但不绝对肯定二者一定相等(可能出现哈希碰撞)。输入一些数据计算出散列值,然后部分改变输入值,一个具有强混淆特性的散列函数会产生一个完全不同的散列值。

    更多散列函数信息

    xia

    读到这里了,如果对你有帮助就留个赞吧。

    另外关注公众号java内功心法回复我要当架构即可领取java方向必备进阶资料。

    展开全文
  • 断点续传实现原理

    2020-12-03 17:05:45
    我们都用过网盘,不管是例如百度网盘之类的公共网盘,还是自己搭建的私有网盘,都会或多或少的涉及到断点续传和秒断点续传和秒大大提高了网盘上传的效率,下面我们就来讲解一下这两种技术的具体原理和实现,...

    我们都用过网盘,不管是例如百度网盘之类的公共网盘,还是自己搭建的私有网盘,都会或多或少的涉及到断点续传和秒传。断点续传和秒传大大提高了网盘上传的效率,下面我们就来讲解一下这两种技术的具体原理和实现,这里的讲解不涉及任何前后端编程语言,适合所有语言开发人员阅读。

    零、断点续传

    这里以上传为例,下载方式的断点续传类似。

    简述原理

    断点续传说白了就是将一个文件按照一定的规则人为的分割成多个小文件,然后客户端每次只上传一个小文件(当然我们也可以利用多线程技术每次上传多个小文件),服务器接收到上传过来的小文件后根据一定的规则来组合这些小文件。如果在上传过程中出现网络中断等意外情况,下次再次上传时可以从已经上传的部分继续上传,而不是重新上传。

    详细讲解

    从 HTTP1.1 协议开始就已经支出获取文件的部分内容,断点续传技术就是利用 HTTP1.1 协议的这个特点在 Header 里添加两个参数来实现的。这两个参数分别是客户端请求时发送的 Range 和服务器返回信息时返回的 Content-Range - Range,Range 用于指定第一个字节和最后一个字节的位置,格式如下:

    Range:(unit=first byte pos)-[last byte pos]

    Range 常用的格式有如下几种情况:

    Range:bytes=0-1024 ,表示传输的是从开头到第1024字节的内容;Range:bytes=1025-2048 ,表示传输的是从第1025到2048字节范围的内容;Range:bytes=-2000 ,表示传输的是最后2000字节的内容;Range:bytes=1024- ,表示传输的是从第1024字节开始到文件结束部分的内容;Range:bytes=0-0,-1 表示传输的是第一个和最后一个字节 ;Range:bytes=1024-2048,2049-3096,3097-4096 ,表示传输的是多个字节范围。Content-Range Content-Range 用于响应带有 Range 的请求。服务器会将 Content-Range 添加在响应的头部,格式如下:

    Content-Range:bytes(unit first byte pos)-[last byte pos]/[entity length]

    常见的格式内容如下:

    Content-Range:bytes 2048-4096/10240

    这里边 2048-4096 表示当前发送的数据范围, 10240 表示文件总大小。

    这里我顺便说一下,如果在客户端请求报文头中,对 Range 填入了错误的范围值,服务器会返回 416 状态码。416 状态码表示服务器无法处理所请求的数据区间,常见的情况是请求的数据区间不在文件范围之内,也就是说,Range 值,从语法上来说是没问题的,但从语义上来说却没有意义。

    注意:当使用断点续传的方式上传下载软件时 HTTP 响应头将会变为:

    HTTP/1.1 206 Partial Content

    当然光有 Range 和 Content-Range 还是不够的,我们还要知道服务端是否支持断点续传,只需要从如下两方面判断即可:

    判断服务端是否只 HTTP/1.1 及以上版本,如果是则支持断点续传,如果不是则不支持 服务端返回响应的头部是否包含 Access-Ranges ,且参数内容是 bytes 符合以上两个条件即可判定位支持断点续传。

    校验

    这里的校验主要针对断点续传下载来说的。当服务器端的文件发生改变时,客户端再次向服务端发送断点续传请求时,数据肯定就会发生错误。这时我们可以利用 Last-Modified 来标识最后的修改时间,这样就可以判断服务器上的文件是否发生改变。和 Last-Modified 具有同样功能的还有 if-Modified-Since,它俩的不同点是 Last-Modified 由服务器发送给客户端,而 if-Modified-Since 是由客户端发出, if-Modified-Since 将先前服务器发送给客户端的 Last-Modified 发送给服务器,服务器进行最后修改时间验证后,来告知客户端是否需要重新从服务器端获取新内容。客户端判断是否需要更新,只需要判断服务器返回的状态码即可,206 代表不需要重新获取接着下载就行,200代表需要重新获取。 但是 Last-Modified 和 if-Modified-Since 存在一些问题:

    某些文件只是修改了修改时间而内容却没变,这时我们并不希望客户端重新缓存这些文件;

    某些文件修改频繁,有时一秒要修改十几次,但是 if-Modified-Since 是秒级的,无法判断比秒更小的级别; 部分服务器无法获得精确的修改时间。 要解决上述问题我们就需要用到 Etag ,只需将相关标记(例如文件版本号等)放在引号内即可。

    当使用校验的时候我们不需要手动实现验证,只需要利用 if-Range 结合 Last-Modified 或者 Etage 来判断是否发生改变,如果没有发生改变服务器将向客户端发送剩余的部分,否则发送全部。

    注意:If-Range 必须与 Range 配套使用。缺少其中任意一个另一个都会被忽略。

    秒传

    原理

    秒传利文件的MD5,首先将文件的MD5发送个服务器,服务器传输过来的MD5判断服务器上是否存在相同类型的文件,如果存在就将文件复制一份,而不是本地上传。这样就是先的秒传功能。

    MD5

    秒传涉及到了MD5,那么什么MD5呢?MD5的英文全称是 Message-Digest Algorith 5 ,是计算机广泛使用的算法之一。 MD5 会为文件产生唯一的“指纹”,任何改动都会改变文件指纹。它以 512位分组来处理信息,每个分组又被分为16个32位分组,经过处理后输出4个32位分组,最后将输出的4个32位分组进行级联生成128位散列值。

    MD5的具有压缩性、易计算、抗修改、弱抗碰撞和强抗碰撞。下面我们一一来讲解:

    压缩性:任意长度数据,生成的MD5值长度是固定的;易计算:可以很方便的从原始数据计算出MD5;抗修改:对原始数据的任何修改,都会改变MD5;弱抗碰撞和强抗碰撞:很难找到具有相同MD5的数据。破解谣言:有人说网盘能秒传证明数据在网盘服务器是不加密的,有数据库查看权限的人都可以看,所以私密文件最好在本地磁盘加密后再上传到网盘中。这句话是错误的,正规的网盘服务器只是验证了文件的MD5码,文件还是加密存放的。

    总结

    这篇文章讲解了断点续传和秒传的知识,也讲解了它们所使用技术的相关知识点。这些知识可以用在任何编程语言的断点续传和秒传的开发中,因此这篇文章我并没有根据具体的语言讲解。

    展开全文
  • 继木马编程DIY的上两篇,现在我们开始讨论断点续传与多线程文件传输的实现.其实这两项功能是下载软件所 必不可少的功能了,现在我们把它加到自己的木马中来感受感受.提到多线程下载,首先向网络蚂蚁的作者 洪以容前辈...

    转载自:http://blog.csdn.net/zhengkangchen/article/details/3942252

    继木马编程DIY的上两篇,现在我们开始讨论断点续传与多线程文件传输的实现.其实这两项功能是下载软件所

    必不可少的功能了,现在我们把它加到自己的木马中来感受感受.提到多线程下载,首先向网络蚂蚁的作者
    洪以容前辈致敬,正是由于网络蚂蚁而使得多线程下载被关注并流行起来.在这本篇文章中我们将简单的实现
    支持断点续传和多线程传输的程序.为了更清晰的说明问题,我们将断点续传与多线程传输分别用两个程序来实现

    多线程传输实现
    实现原理
    将源文件按长度为分为N块文件,然后开辟N个线程,每个线程传输一块,最后合并所有线线程文件.比如
    一个文件500M我们按长度可以分5个线程传输.第一线程从0-100M,第二线程从100M-200M......最后合并5个线程文件.
    实现流程
    1.客户端向服务端请求文件信息(名称,长度)
    2.客户端跟据文件长度开辟N个线程连接服务端
    3.服务端开辟新的线程与客户端通信并传输文件
    4.客户端将每线程数据保存到一个文件
    5.合并所有线程文件
    编码实现
    大体说来就是按以上步骤进行,详细的实现和一些要点,我们跟据以上流程在编码中实现

    结构定义
    在通信过程中需要传递的信息包括文件名称,文件长度,文件偏移,操作指令等信息,为了方便操作我们定义如下结构 

    代码:
    typedef struct
    {
            char        Name[100];        //文件名称
            int                FileLen;        //文件长度
            int                CMD;                //操作指令
            int                seek;                //线程开始位置
            SOCKET sockid;                
    }FILEINFO;
    1.请求文件信息
    客户端代码如下 

    代码:
            FILEINFO fi;
            memset((char*)&fi,0,sizeof(fi));
            fi.CMD=1;                //得到文件信息
            if(send(client,(char*)&fi,sizeof(fi),0)==SOCKET_ERROR)
            {
                    cout<<"Send Get FileInfo Error/n";
            }
    服务端代码如下
            while(true)
            {
                    SOCKET client;
                    if(client=accept(server,(sockaddr *)&clientaddr,&len))
                    {
                            FILEINFO RecvFileInfo;
                            memset((char*)&RecvFileInfo,0,sizeof(RecvFileInfo));
                            if(recv(client,(char*)&RecvFileInfo,sizeof(RecvFileInfo),0)==SOCKET_ERROR)
                            {
                                    cout<<"The Clinet Socket is Closed/n";
                                    break;
                            }else
                            {
                                    EnterCriticalSection(&CS);                //进入临界区
                                    memcpy((char*)&TempFileInfo,(char*)&RecvFileInfo,sizeof(RecvFileInfo));
                                    switch(TempFileInfo.CMD)
                                            {
                                            case 1:
                                                     GetInfoProc        (client);
                                                     break;
                                            case 2:
                                                     TempFileInfo.sockid=client;
                                                     CreateThread(NULL,NULL,GetFileProc,NULL,NULL,NULL);
                                                     break;
                                            }
                                    LeaveCriticalSection(&CS);                //离开临界区
                            }
                    }
            }
    在这里服务端循环接受连接,并跟据TempFileInfo.CMD来判断客户端的请求类型,1为请求文件信息,2为下载文件
    因为在下载文件的请求中,需要开辟新的线程,并传递文件偏移和文件大小等信息,所以需要对线程同步.这里使用临界区
    其文件信息函数GetInfoProc代码如下 

    代码:
    DWORD GetInfoProc(SOCKET client)
    {
            CFile        file;
            if(file.Open(FileName,CFile::modeRead|CFile::typeBinary))
            {
                    int FileLen=file.GetLength();
                    if(send(client,(char*)&FileLen,sizeof(FileLen),0)==SOCKET_ERROR)
                    {
                            cout<< "Send FileLen Error/n";
                    }else
                    {
                            cout<< "The Filelen is "<<FileLen<<"/n/n";
                    }
            }
            return 0;
    }
    这里主要是向客户端传递文件长度,而客户端收到长度后则开辟线程进行连接传输文件
    2.客户端跟据长度开辟线程
    其实现代码如下 

    代码:
            FILEINFO FI;
            int FileLen=0;
            if(recv(client,(char*)&FileLen,sizeof(FileLen),0)==SOCKET_ERROR)//接受文件长度
            {
                    cout<<"Recv FileLen Error/n";
            }else
            {
                    cout<<"FileLen is "<<FileLen<<"/n";
                    int COUNT_SIZE=FileLen/5;                        //每线程传输大小                        
                    for(int i=0;i<5;i++)                                //分5个线程传输
                    {
                            EnterCriticalSection(&CS);                //进入临界区
                            memset((char*)&FI,0,sizeof(FI));
                            FI.CMD=2;                                //请求下载文件
                            FI.seek=i*COUNT_SIZE;                        //线程文件偏移
                            if(i+1==5)                                //最后一线程长度为总长度减前4个线程长度
                            {
                                    FI.FileLen=FileLen-COUNT_SIZE*i;
                            }else
                            {
                                    FI.FileLen=COUNT_SIZE;
                            }
                            Thread=CreateThread(NULL,NULL,GetFileThread,&i,NULL,NULL);
                            Sleep(500);
                            LeaveCriticalSection(&CS);                //离开临界区
                    }
            }
            WaitForMultipleObjects(5,Thread,true,INFINITE);                //等所有线程结束
    这里默认开辟5个线程传输,当然可以改为想要的线程数目,仍然用临界区来实现线程的同步问题
    3.服务端开辟线程传输数据
    在1.请求文件信息中以说明了服务端的结构,这里主要介绍线程函数的实现,其代码如下 

    代码:
    DWORD WINAPI GetFileProc(LPVOID lparam)
    {
            EnterCriticalSection(&CS);                        //进入临界区
            int FileLen=TempFileInfo.FileLen;
            int Seek=TempFileInfo.seek;
            SOCKET client=TempFileInfo.sockid;
            LeaveCriticalSection(&CS);                        //离开临界区
            CFile        file;
            if(file.Open(FileName,CFile::modeRead|CFile::typeBinary))
            {
                    file.Seek(Seek,CFile::begin);                //指针移至偏移位置
                    char *date=new char[FileLen];
                    int nLeft=FileLen;
                    int idx=0;
                    file.Read(date,FileLen);
                    while(nLeft>0)
                    {
                            int ret=send(client,&date[idx],nLeft,0);
                            if(ret==SOCKET_ERROR)
                            {
                                    cout<<"Send Date Error /n";
                                    break;
                            }
                            nLeft-=ret;
                            idx+=ret;
                    }
                    file.Close();
                    delete[] date;
            }else
            {
                    cout<<"open the file error/n";
            }
            closesocket(client);
            return 0;
    }
    还是比较简单的,主要是获取线程的文件长度和偏移,并移动文件指针到偏移处,最后读取发送数据,而客户端
    接受数据并写入文件.

    4.客户端将线程数据保存到文件
    GetFileThread的实现代码如下 

    代码:
    DWORD WINAPI GetFileThread(LPVOID lparam)
    {
            char TempName[MAX_PATH];
            sprintf(TempName,"TempFile%d",*(DWORD*)lparam);        //每线程的文件名为"TempName"+线程数
            SOCKET client;
            SOCKADDR_IN serveraddr;
            int port=5555;
            client=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
            serveraddr.sin_family=AF_INET;
            serveraddr.sin_port=htons(port);
            serveraddr.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
            if(connect(client,(SOCKADDR*)&serveraddr,sizeof(serveraddr))==INVALID_SOCKET)
            {
                    cout<<"Connect Server Error/n";
            }
            EnterCriticalSection(&CS);                //进入临界区
            if(send(client,(char*)&FI,sizeof(FI),0)==SOCKET_ERROR)
            {
                    cout<<"Send GetFile Error/n";
                    return 0;
            }
            CFile        file;
            int FileLen=FI.FileLen;                        //文件长度
            int Seek=FI.seek;                        //文件偏移
            LeaveCriticalSection(&CS);                //离开临界区
            if(file.Open(TempName,CFile::modeWrite|CFile::typeBinary|CFile::modeCreate))
            {
                    char *date = new char[FileLen];
                    int nLeft=FileLen;
                    int idx=0;
                    while(nLeft>0)
                    {
                            int ret=recv(client,&date[idx],nLeft,0);
                            if(ret==SOCKET_ERROR)
                            {
                                    cout<<"Recv Date Error";
                                    break;
                            }
                            idx+=ret;
                            nLeft-=ret;
                    }
                    file.Write(date,FileLen);
                    file.Close();
                    delete[] date;
            }else
            {
                    cout<<"Create File Error/n";
            }
            return 0;
    }
    在此线程函数中,将每线程传输的数据存为一个文件,文件名为"TempName"+线程数,只所以存成单独的文件是
    因为比较直观且容易理解,但如果文件很大的话这个方法并不好,因为合并文件又会花费很多时间,另一个方法
    是 创始一个文件,让每个线程写入文件的不同偏移,这样就可以不必单独合并文件了,但要记得打开文件时
    加入CFile::shareDenyNone属性.这样整个过程就完成了.最后一步合并线程文件
    5.合并线程文件 

    代码:
    int UniteFile()                //合并线程文件
    {
            cout<<"Now is Unite Fileing.../n";
            int                len;
            char        *date;
            CFile        file;
            CFile        file0;
            /*其它文件......*/
            if(file.Open(FileName,CFile::modeCreate|CFile::typeBinary|CFile::modeWrite))//创建文件
            {
                    file0.Open("TempFile0",CFile::modeRead|CFile::typeBinary);//合并第一线程文件
                    len=file0.GetLength();
                    date=new char[len];
                    file0.Read(date,len);
                    file.SeekToEnd();
                    file.Write(date,len);
                    file1.Open("TempFile1",CFile::modeRead|CFile::typeBinary);//合并第二线程文件
                    len=file1.GetLength();
                    date=new char[len];
                    file1.Read(date,len);
                    file.SeekToEnd();
                    file.Write(date,len);
                    /*合并其它线程......*/
                    
                    file0.Close();
                    file1.Close();
                    /*.......*/
                    delete[] date;
                    
                    return true;
            }else
            {
                    return false;
            }
    }
    这个简单,就是打开一个文件读取到缓冲区,写入文件,再打开第二个......现在多线程传输部分就介绍完了
    下面讨论断断点续传的实现
    展开全文
  • js断点续传

    千次阅读 2018-11-02 15:18:26
    js 断点续传 1. js断点续传的思路 对于一个大文件,可以将其切割成适当的小文件,提交给后台,再由后台合并成大文件。 这样做法的好处有: (1)化整为零,可以防止文件上传中断而导致整个文件都需要重新上传的杯具...
  • 我们都用过网盘,不管是例如百度网盘之类的公共网盘,还是自己搭建的私有网盘,都会或多或少的涉及到断点续传和秒断点续传和秒大大提高了网盘上传的效率,下面我们就来讲解一下这两种技术的具体原理和实现,...
  •  //创建一个提供信息摘要算法的对象,初始化为md5算法对象  MessageDigest md = MessageDigest.getInstance("MD5");  fis = new FileInputStream(file);  byte[] buff = new byte[1024];  ...
  • 在下载有用的软件的时候常常因为网络或者其他原因突然断网,已经下载了一部分的软件又要重新下载,于是断点续传出现在网络上,大量的下载工具软件都支持这个功能,其意义就是可以继续上次没有下载完的文件下载,节省...
  • QT 断点续传

    千次阅读 2018-10-24 09:34:49
    因此,为了实现断点续传我们需要重新实现文件传输,并在其中添加断点续传的控制。其实Ftp文件传输的本质也是使用Tcp来实现底层的文件传输。大体思路就是:利用Qftp的connectToHost登录ftp服务器,使用login登录ftp...
  • 实现FTP断点续传

    2021-06-04 08:19:04
    因此,为了实现断点续传我们需要重新实现文件传输,并在其中添加断点续传的控制。其实Ftp文件传输的本质也是使用Tcp来实现底层的文件传输。大体思路就是:利用Qftp的connectToHost登录ftp服务器,使用login登录ftp...
  • 本文基于HTML5 的FILE API 实现了一个HTTP断点续传系统,支持HTTP超大文件断点续传,兼容目前主流浏览器,可移植性非常好,可以适应与PC、iOS与Android系统。
  • 范围请求及断点续传4. 多段请求 1. 数据压缩 通常浏览器在发送请求时都会带着 “Accept-Encoding” 头字段,里面是浏览器支持的压缩格式列表,例如 gzip、deflate、br 等,这样服务器就可以从中选择一种压缩算法,...
  • 目前市场上大多数的网站的断点上传都是需要安装浏览器插件的,本文就针对高级浏览器的环境下,通过HTML5 File api实现断点上传进行说明一、实现文件多选HTML5的新增了"multiple"属性,该属性可接受多个值的文件上传...
  • 断点续传JAVA实现

    2017-12-25 15:40:56
    public class BreakpointResume { private static Logger log = Logger.getLogger(BreakpointResume.... /**下载操作,支持断点续传*/ public static void download(HttpServletRequest req,HttpServletResponse r
  • 随着视频网站和大数据应用的兴起,特别是高清...在网络抖动的情况下如何保障断点续传?如何通过多终端快速上传?这是现实中常常遇到的问题。 为了通用性更强,采用安装特定的浏览器上传插件方式并不可取,最好的实...
  • 高清智能手机人人都有,各种场景下的视频应用越来越多,而且视频分辨率和视频尺寸越来越大(1080P高清已经普及,4K视频也即将普及),而且我们又需要和他人之间进行视频分享,这样就常常遇到大文件上传和断点续传的...
  • 支持大文件断点续传,要求刷新浏览器,重启浏览器,重启电脑后仍然能够继续上传。文件大小要求能够支持到50个G。 支持自动加载本地文件,要求能够自动加载指定的本地文件。 支持文件批量下载,要求不要
  • 一般最有直观感受的就是迅雷下载大文件的断点续传设计,答主之前也实现过FTP/TFTP协议,重点就是续记录包的设计实现。各大客户端的数据续分为下面几种: 1、 续前查询 发送大数据流失败后,先查询服务端...
  • 使用HASH算法实现文件的断点续传

    千次阅读 2012-12-13 12:40:49
    使用断点续传技术,充分利用网络带宽和CPU。 思路:把大文件按照一定算法分割成很多部分Part文件,用MD5算法签名做各个part部分的验证值,客户端下载后用同一个key对下载的part做MD5,结果一样,就完成这一个part的...
  • 【大话QT之十】实现FTP断点续传

    千次阅读 2014-06-17 23:25:24
    因此,为了实现断点续传我们需要重新实现文件传输,并在其中添加断点续传的控制。其实Ftp文件传输的本质也是使用Tcp来实现底层的文件传输。大体思路就是:利用Qftp的connectToHost登录ftp服务器,使用login登录ftp...
  • iOS端实现大文件上传和断点续传

    千次阅读 2019-07-03 11:11:03
    我们当前正处理视频爆炸的时代,各种场景下的视频应用越来越多,而且视频分辨率和视频尺寸越来越大,而且我们又需要和他人之间进行视频分享,这样就常常遇到大文件上传和断点续传的问题。因为文件过大(比如2GB以上...
  • web直OSS(断点续传)

    千次阅读 2019-08-29 10:07:02
    web直OSS(断点续传) 发布时间:2018-12-28 10:31:38 不多说,直接上代码 <input type="file" id="file" /> <script src="http://gosspublic.alicdn.com/aliyun-oss-sdk-6.0.2.min.js"></script>...
  • webuploader 断点续传

    2019-10-10 13:09:06
    webuploader 实现 断点续传 webuploader是百度开发的上传文件前端控件。可支持html5和flash,因此对浏览器的兼容比较好。因为需要用到ie8,ie8不支持html5, 所以必须支持flash上传。 断点续传原理: 1)将大分件分...
  • java+断点续传

    千次阅读 2019-08-15 17:38:54
    在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传和下载功能的实现。...支持大文件断点续传,要求刷新浏览器,重启浏览器,重启电脑后仍然能够继续上传。文件大小...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,343
精华内容 4,137
关键字:

断点续传算法