基于Web Uploader实现断点上传

WebUploader

可以总体分为四步:
1)文件上传注册

  • 判断文件是否存在。

2)检查分块是否存在

  • 检查当前上传分块是否存在

3)上传分块

  • 将分块存放到指定目录

4)合并分块

  • 将零散的分块合并为完整的文件。

前端代码:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>WebUploader演示</title>
    <link rel="stylesheet" type="text/css" href="/static/css/webuploader/webuploader.css" />
    <script type="text/javascript" src="/static/plugins/jquery/dist/jquery.js"></script>
    <script type="text/javascript" src="/static/plugins/webuploader/dist/webuploader.js"></script>

</head>
<body>
<!-- 上传文件区域 -->
<div id="uploader">
    <!-- 用于选择文件 -->
    <div id="filePicker">选择文件</div>
    <!-- 用于显示文件列表 -->
    <div id="fileList"></div>
</div>
<!-- 3.添加js代码 -->
<script type="text/javascript">
    // 监听分块上传的时间点,断点续传
    var fileMd5;
    WebUploader.Uploader.register({
            "before-send-file":"beforeSendFile",
            "before-send":"beforeSend",
            "after-send-file":"afterSendFile"
        },{
            //对文件分块之前调用,查看文件目录是否存在
            beforeSendFile:function(file) {
                // 创建一个deffered,用于通知是否完成操作
                var deferred = WebUploader.Deferred();
                // 计算文件的唯一标识,用于断点续传和秒传
                (new WebUploader.Uploader()).md5File(file, 0, 100*1024*1024)
//                    .progress(function(percentage){
//                        alert(percentage)
//                        console.log("progress="+percentage)
//                        //$("#"+file.id).find("span.state").text("正在获取文件信息...");
//                    })
                    .then(function(val) {
                        fileMd5 = val;

                        alert(fileMd5 )
                        //向服务端请求注册 上传文件
                        $.ajax(
                            {
                                type:"POST",
                                url:"/openapi/media/upload/register",
                                data:{
                                    // 文件唯一标识
                                    fileMd5:fileMd5,
                                    fileName: file.name,
                                    fileSize:file.size,
                                    mimetype: file.type,
                                    fileExt:file.ext
                                },
                                dataType:"json",
                                success:function(response) {
                                    if(response.success) {
                                        //$("#" + file.id).find("span.state").text("成功获取文件信息"+fileMd5);
                                        alert('上传文件注册成功开始上传');
                                        deferred.resolve();
                                    } else {
                                        alert('上传文件注册失败');
                                        deferred.reject();
                                    }
                                }
                            }
                        );
                        // 状态改为已完成
                        //deferred.resolve();
                    });
//                //向formData中放数据,每次上传分块都传这两个参数
//                this.owner.options.formData.fileName = file.name;
//                this.owner.options.formData.fileSize = file.size;
                //生产新的deferred对象防止外部修改状态
                return deferred.promise();
            },
        //在上传文件分块之前调用,检查分块是否存在
            beforeSend:function(block) {
                var deferred = WebUploader.Deferred();

                // 支持断点续传,发送到后台判断是否已经上传过
                $.ajax(
                    {
                        type:"POST",
                        url:"/openapi/media/upload/checkchunk",
                        data:{
                            // 文件唯一标识
                            fileMd5:fileMd5,
                            // 当前分块下标
                            chunk:block.chunk,
                            // 当前分块大小
                            chunkSize:block.end-block.start
                        },
                        dataType:"json",
                        success:function(response) {
                            if(response.ifExist) {
                                // 分块存在,跳过该分块
                                deferred.reject();
                            } else {
                                // 分块不存在或不完整,重新发送
                                deferred.resolve();
                            }
                        }
                    }
                );

                // 发送文件md5字符串到后台
                this.owner.options.formData.fileMd5 = fileMd5;
                return deferred.promise();
            },
        //在所有分块上传完成之后调用,合并分块,删除分块
            afterSendFile:function() {
                // 通知合并分块
                $.ajax(
                    {
                        type:"POST",
                        url:"/openapi/media/upload/mergechunks",
                        data:{
                            fileMd5:fileMd5,
                            fileName :file.name,
                            fileSize:file.size,
                            mimetype:file.type,
                            fileExt:file.ext
                        },
                        success:function(response){

                        }
                    }
                );
            }
        }
    );

    // 上传基本配置
    var uploader = WebUploader.create(
        {
            swf:"/static/plugins/webuploader/dist/Uploader.swf",
            //上传分块的地址
            server:"/openapi/media/upload/uploadchunk",
            //文件上传域的name
            fileVal:"file",
            //选择文件的按钮容器
            pick:"#filePicker",
            //手动触发上传
            auto:false,
//            dnd:"#dndArea",
            //禁止页面的拖拽功能
            disableGlobalDnd:true,

            //是否分块上传
            chunked:true,
            //每块文件大小(默认5M)
            chunkSize:1*1024*1024,
            // 开启几个并发线程(默认3个)
            threads:3,
            // 在上传当前文件时,准备好下一个文件
            prepareNextFile:true
        }
    );

    // 生成缩略图和上传进度
    uploader.on("fileQueued", function(file) {
            // 把文件信息追加到fileList的div中
            $("#fileList").append("<div id='" + file.id + "'><img/><span>" + file.name + "</span>" +
                "<div><span class='percentage'>0%</span></div>" +
                "<div><input type='button' οnclick=\"upload('"+file.id+"')\" value='开始'/></div>" +
                "</div>");

        }
    );
    uploader.on("beforeFileQueued", function(file) {
       var files = uploader.getFiles();
       if(files ){
           if(files.length>0){
               alert("一次只允许上传一个文件");
               return false;
           }
       }
    });

    // 监控上传进度
    // percentage:代表上传文件的百分比
    uploader.on("uploadProgress", function(file, percentage) {
        $("#" + file.id).find("span.percentage").text(Math.round(percentage * 100) + "%");
    });
    uploader.on("uploadError", function(file,reason) {
        console.log(reason)
        alert("上传文件失败,请刷新页面重试!");
    });
    uploader.on("uploadSuccess", function(file,response ) {
        console.log(response)
        alert("上传文件成功!");
    });

    //开始上传
    function upload(fileId){
        uploader.upload(fileId);
    }

</script>
</body>



</html>

后端代码:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@RestController
@RequestMapping("/media/upload")
public class MediaUploadController implements MediaUploadControllerApi {

    @Autowired
    private MediaUploadService mediaUploadService;

    /**
     * 文件上传注册
     *
     * @param fileMd5  文件Md5
     * @param fileName
     * @param fileSize
     * @param mimetype
     * @param fileExt  文件扩展名
     * @return
     */
    @PostMapping("/register")
    @Override
    public ResponseResult register(@RequestParam("fileMd5") String fileMd5, @RequestParam("fileName") String fileName,
                                   @RequestParam("fileSize") String fileSize, @RequestParam("mimetype") String mimetype,
                                   @RequestParam("fileExt") String fileExt) {
        return mediaUploadService.register(fileMd5, fileName, fileSize, mimetype, fileExt);
    }

    /**
     * 检查分块
     *
     * @param fileMd5
     * @param chunk
     * @param chunkSize
     * @return
     */
    @PostMapping("/checkchunk")
    @Override
    public CheckChunkResult checkChunk(@RequestParam("fileMd5") String fileMd5, @RequestParam("chunk") Integer chunk,
                                       @RequestParam("chunkSize") Integer chunkSize) {
        return mediaUploadService.checkChunk(fileMd5, chunk, chunkSize);
    }

    /**
     * 上传分块
     *
     * @param file
     * @param chunk
     * @param fileMd5
     * @return
     */
    @PostMapping("/uploadchunk")
    @Override
    public ResponseResult uploadChunk(@RequestParam("file") MultipartFile file,
                                      @RequestParam("chunk") Integer chunk, @RequestParam("fileMd5") String fileMd5) {
        return mediaUploadService.uploadChunk(file, chunk, fileMd5);
    }

    /**
     * 合并分块
     *
     * @param fileMd5
     * @param fileName
     * @param fileSize
     * @param mimetype
     * @param fileExt
     * @return
     */
    @PostMapping("/mergechunks")
    @Override
    public ResponseResult mergeChunks(@RequestParam("fileMd5") String fileMd5, @RequestParam("fileName") String fileName,
                                      @RequestParam("fileSize") Long fileSize, @RequestParam("mimetype") String mimetype,
                                      @RequestParam("fileExt") String fileExt) {
        return mediaUploadService.mergeChunks(fileMd5, fileName, fileSize, mimetype, fileExt);
    }

}

import org.springframework.web.multipart.MultipartFile;

public interface MediaUploadService {


    /**
     * 文件注册
     * 1.检查上传文件是否存在
     * 2.不存在则创建文件目录
     * @param fileMd5
     * @param fileName
     * @param fileSize
     * @param mimetype
     * @param fileExt
     * @return
     */
    ResponseResult register(String fileMd5, String fileName, String fileSize, String mimetype, String fileExt);

    /**
     * 检查分块
     * 1.检查当前分块是否已经上传
     * 2.未上传检查分块上传路径是否存在,不存在则创建
     * @param fileMd5 文件Md
     * @param chunk 当前分块下标
     * @param chunkSize 分块大小
     * @return
     */
    CheckChunkResult checkChunk(String fileMd5, Integer chunk, Integer chunkSize);

    /**
     * 上传分块
     * @param file
     * @param chunk
     * @param fileMd5
     * @return
     */
    ResponseResult uploadChunk(MultipartFile file, Integer chunk, String fileMd5);

    /**
     * 合并分块
     * @param fileMd5
     * @param fileName
     * @param fileSize
     * @param mimetype
     * @param fileExt
     * @return
     */
    ResponseResult mergeChunks(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt);

}



import com.alibaba.fastjson.JSON;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.*;


@Service
public class MediaUploadServiceImpl implements MediaUploadService {

    private static final Logger logger = LoggerFactory.getLogger(MediaUploadServiceImpl.class);

    /**
     * 自定义文件上传路径
     */
    @Value("${sfs-service-manage-media.upload-location}")
    String uploadPath;

    @Autowired
    private MediaFileRepository mediaFileRepository;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 视频处理路由
     */
    @Value("${sfs-service-manage-media.mq.routingkey-media-video}")
    public String routingkey_media_video;

    @Override
    public ResponseResult register(String fileMd5, String fileName, String fileSize, String mimetype, String fileExt) {
        //1.获取文件全路径,判断文件是否存在
        String filePath = getFilePath(fileMd5, fileExt);
        File file = new File(filePath);
        Optional<MediaFile> optionalMediaFile = mediaFileRepository.findById(fileMd5);
        //判断文件是否存在在指定路径和文件信息是否存在于数据库
        if (file.exists() && optionalMediaFile.isPresent()) {
            //提示上传的文件已存在
            ExceptionCast.cast(MediaCode.UPLOAD_FILE_REGISTER_EXIST);
        }
        //2.根据文件信息创建文件上传目录
        boolean fileFold = createFileFold(fileMd5);
        if (!fileFold) {
            //提示文件注册失败
            ExceptionCast.cast(MediaCode.UPLOAD_FILE_REGISTER_FAIL);
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }

    @Override
    public CheckChunkResult checkChunk(String fileMd5, Integer chunk, Integer chunkSize) {
        //获取块文件存放目录
        String chunkFileFolderPath = getChunkFileFolderPath(fileMd5);
        //根据块文件目录创建对象(当前块文件所在路径 = 块文件路径+块文件下标)
        File file = new File(chunkFileFolderPath + chunk);
        //判断当前分块文件是否已经存在
        if (file.exists()) {
            //块文件已存在
            return new CheckChunkResult(CommonCode.SUCCESS, true);
        } else {
            //块文件不存在
            return new CheckChunkResult(CommonCode.SUCCESS, false);
        }
    }
/*
	分块上传
*/
    @Override
    public ResponseResult uploadChunk(MultipartFile file, Integer chunk, String fileMd5) {
        if (file == null) {
            //提示上传文件内容为空
            ExceptionCast.cast(MediaCode.UPLOAD_CHUNK_FILE_IS_NULL);
        }
        //创建块文件目录
        boolean chunkFilePath = createChunkFilePath(fileMd5);
        if (!chunkFilePath) {
            //创建块文件目录失败
            ExceptionCast.cast(MediaCode.CREATE_CHUNK_FILE_FAIL);
        }
        //块文件路径 = 块文件目录+块文件下标(没有扩展名)
        File chunkFile = new File(getChunkFileFolderPath(fileMd5) + chunk);
        InputStream inputStream = null;
        FileOutputStream outputStream = null;
        try {
            //获取文件输入流
            inputStream = file.getInputStream();
            //根据块文件路径创建文件输出流
            outputStream = new FileOutputStream(chunkFile);
            //将文件从输入流复制到文件输出流,输出到指定目录
            IOUtils.copy(inputStream, outputStream);
        } catch (IOException e) {
            e.printStackTrace();
            //提示上传块文件失败
            ExceptionCast.cast(MediaCode.UPLOAD_CHUNK_FILE_FAIL);
        } finally {
            try {
                //关闭输入流
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                //关闭输出流
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }
/*
合并分块
*/
    @Override
    public ResponseResult mergeChunks(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) {
        //获取块文件的路径
        String chunkFileFolderPath = getChunkFileFolderPath(fileMd5);
        File chunkFileFolder = new File(chunkFileFolderPath);
        //如果块文件目录不存在,则创建
        if (!chunkFileFolder.exists()) {
            chunkFileFolder.mkdirs();
        }
        //获得文件所在全路径
        String filePath = getFilePath(fileMd5, fileExt);
        //创建合并文件File对象
        File mergeFile = new File(filePath);
        //合并后的文件若存在先删除
        if (mergeFile.exists()) {
            //删除之前的合并文件
            mergeFile.delete();
        }
        boolean newFile = false;
        try {
            //根据合并后的文件路径创建新的合并文件(文件全路径 = D:/sfs/video/0/1/01..../01....mp4)
            newFile = mergeFile.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (!newFile) {
            //提示合并文件创建失败
            ExceptionCast.cast(MediaCode.MERGE_FILE_CERETE_FAIL);
        }
        //获取块文件目录下的块文件集合(升序排列)
        List<File> chunkFileList = getChunkFileList(chunkFileFolder);
        //合并文件
        mergeFile = mergeFile(chunkFileList, mergeFile);
        if (mergeFile == null) {
            //提示合并文件失败,合并过程出错
            ExceptionCast.cast(MediaCode.MERGE_FILE_IS_FAIL);
        }
        //合并文件成功之后,删除分块文件
        boolean deleteChunks = deleteChunks(fileMd5);
        if (!deleteChunks) {
            //提示删除分块文件出错
            ExceptionCast.cast(MediaCode.DELETE_CHUNK_IS_FAIL);
        }
        //根据合并文件获取Md5值和传入的文件Md5值进行比较校验文件正确性
        Boolean checkMd5 = checkFileMd5(fileMd5, mergeFile);
        if (!checkMd5) {
            //提示合并文件校验失败
            ExceptionCast.cast(MediaCode.MERGE_FILE_CHECKFAIL);
        }
        //保存文件信息到数据库
        MediaFile mediaFile = new MediaFile();
        mediaFile.setFileId(fileMd5);
        //设置文件名称 = 文件Md5值+文件扩展名
        mediaFile.setFileName(fileMd5 + "." + fileExt);
        //设置文件原始名称
        mediaFile.setFileOriginalName(fileName);
        //设置文件路径为相对路径
        mediaFile.setFilePath(getFileRelativePath(fileMd5));
        mediaFile.setFileSize(fileSize);
        mediaFile.setUploadTime(new Date());
        //设置文件媒体类型
        mediaFile.setMimeType(mimetype);
        //设置文件类型(文件扩展名)
            mediaFile.setFileType(fileExt);
    //设置状态为上传成功
        mediaFile.setFileStatus("301002");
        mediaFileRepository.save(mediaFile);
    //向MQ发送消息(合并文件完成后,发送消息通知消费者处理视频文件)
    sendProcessVideoMsg(mediaFile.getFileId());
        return new ResponseResult(CommonCode.SUCCESS);
}


    //******************************************************************************************************************


    /**
     * 根据文件唯一标识和文件扩展名获得文件完整路径
     * 一级目录:md5的第一个字符
     * 二级目录:md5的第二个字符
     * 三级目录:md5
     * 文件名:md5+文件扩展名
     *
     * @param fileMd5 文件md5值
     * @param fileExt 文件扩展名
     * @return
     */
    private String getFilePath(String fileMd5, String fileExt) {
        //文件全路径 = D:/sfs/video/0/1/01..../01....mp4
        String filePath = uploadPath + fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) + "/" + fileMd5 + "/" + fileMd5 + "." + fileExt;
        return filePath;
    }

    /**
     * 得到文件所在的目录
     *
     * @param fileMd5
     * @return
     */
    private String getFileFolderPath(String fileMd5) {
        String fileFoladerPath = uploadPath + fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) + "/" + fileMd5 + "/";
        return fileFoladerPath;
    }

    /**
     * 得到文件相对路径
     *
     * @param fileMd5
     * @return
     */
    private String getFileRelativePath(String fileMd5) {
        String fileRelativePath = fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) + "/" + fileMd5 + "/";
        return fileRelativePath;
    }

    /**
     * 根据文件绝对路径创建文件夹
     *
     * @param fileMd5
     * @return
     */
    private boolean createFileFold(String fileMd5) {
        //获得文件上传目录,绝对路径
        String fileFolderPath = getFileFolderPath(fileMd5);
        File fileFolder = new File(fileFolderPath);
        if (!fileFolder.exists()) {
            boolean mkdirs = fileFolder.mkdirs();
            return mkdirs;
        }
        return true;
    }

    /**
     * 获取块文件目录
     *
     * @param fileMd5
     * @return
     */
    private String getChunkFileFolderPath(String fileMd5) {
        String fileChunkFolderPath = getFileFolderPath(fileMd5) + "chunks" + "/";
        return fileChunkFolderPath;
    }

    /**
     * 创建块文件目录
     *
     * @param fileMd5
     * @return
     */
    private boolean createChunkFilePath(String fileMd5) {
        //获取块文件目录
        String chunkFilePath = getChunkFileFolderPath(fileMd5);
        File file = new File(chunkFilePath);
        //如果块文件目录不存在
        if (!file.exists()) {
            //创建块文件目录
            boolean mkdirs = file.mkdirs();
            return mkdirs;
        }
        return true;
    }

    /**
     * 根据块文件目录获取快文件集合(根据名称排序)
     *
     * @param chunkFileFolder 块文件目录File对象
     * @return
     */
    private List<File> getChunkFileList(File chunkFileFolder) {
        //根据块文件目录对象获取目录下所有文件
        File[] chunkFiles = chunkFileFolder.listFiles();
        //将文件数组转换为集合对象
        List<File> list = Arrays.asList(chunkFiles);
        //创建比较器根据名称对集合进行比较,升序排列
        Collections.sort(list, new Comparator<File>() {
            @Override
            public int compare(File o1, File o2) {
                //根据名称进行比较
                if ((Integer.parseInt(o1.getName()) > Integer.parseInt(o2.getName()))) {
                    return 1;
                }
                return -1;
            }
        });
        return list;
    }

    /**
     * 合并文件
     *
     * @param chunkList 升序排列后的块文件集合
     * @param mergeFile 合并文件的File对象
     * @return
     */
    private File mergeFile(List<File> chunkList, File mergeFile) {
        try {
            //根据合并文件路径创建操作文件对象
            RandomAccessFile rand_write = new RandomAccessFile(mergeFile, "rw");
            //设置指针指向文件首位
            rand_write.seek(0);
            //创建字符缓冲区
            byte[] b = new byte[1024];
            //便利块文件
            for (File file : chunkList) {
                //读取块文件数据
                RandomAccessFile rand_read = new RandomAccessFile(file, "r");
                int len = -1;
                //将块文件数据写入缓冲区,写入成功后,len = 1024
                while ((len = rand_read.read(b)) != -1) {
                    //从字符缓冲区的0子节开始到len长度将字节写入合并文件对象
                    rand_write.write(b, 0, len);
                }
                //关闭读取块文件对象
                rand_read.close();
            }
            //关闭合并文件写入对象
            rand_write.close();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        return mergeFile;
    }

    /**
     * 合并文件成功后删除分块文件
     *
     * @param fileMd5
     * @return
     */
    private boolean deleteChunks(String fileMd5) {
        //获取块文件目录
        String chunkFileFolderPath = getChunkFileFolderPath(fileMd5);
        //分块文件夹对象
        File chunksFile = new File(chunkFileFolderPath);
        //分块文件数组
        File[] chunks = chunksFile.listFiles();
        List<File> list = Arrays.asList(chunks);
        for (File file : list) {
            //循环删除每个分块文件
            file.delete();
        }
        //分块文件全部删除后,删除chunks文件夹
        boolean delete = chunksFile.delete();
        if (delete) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 校验文件中的Md5值和传入的Md5值是否一致
     *
     * @param fileMd5
     * @param mergeFile
     * @return
     */
    private Boolean checkFileMd5(String fileMd5, File mergeFile) {
        if (fileMd5 == null || mergeFile == null) {
            return false;
        }
        InputStream fileInputStream = null;
        try {
            //创建文件输入流
            fileInputStream = new FileInputStream(mergeFile);
            //从文件输入流中获取文件的Md5值
            String md5 = DigestUtils.md5Hex(fileInputStream);
            //若文件Md5值和传入的Md5值一致,返回true
            if (md5.equalsIgnoreCase(fileMd5)) {
                return true;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    /**
     * 生产者向消费者发送消息处理视频
     * 消息内容为视频id
     *
     * @param mediaId
     */
    private void sendProcessVideoMsg(String mediaId) {
        Optional<MediaFile> optional = mediaFileRepository.findById(mediaId);
        if (!optional.isPresent()) {
            //提示查询视频信息失败
            ExceptionCast.cast(MediaCode.SELECT_VIDEO_IS_FAIL);
        }
        Map map = new HashMap();
        map.put("mediaId", mediaId);
        String msg = JSON.toJSONString(map);
        try {
            logger.info("开始发送视频处理的消息...");
            //开始发送消息,指定交换机、路由key、和消息内容
            this.rabbitTemplate.convertAndSend(RabbitmqConfig.EX_MEDIA_PROCESSTASK, routingkey_media_video, msg);
            logger.info("发送视频处理消息结束....");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除文件夹及文件夹里内容
     *
     * @param args
     */
    public static void main(String[] args) {
        File file = new File("D:\\sfs\\video\\c\\5\\c5c75d70f382e6016d2f506d134eee11\\chunks");
        File[] files = file.listFiles();
        List<File> list = Arrays.asList(files);
        for (File file1 : list) {
            file1.delete();
        }
        file.delete();
    }


}


/**
 * 合并块文件步骤
 * 1.获取块文件目录下的所有文件
 * 2.将所有块文件根据名称进行排序(块文件只有下标,排序后为0,1,2,3...)
 * 3.根据合并文件目录创建写对象
 * 4.遍历块文件
 * 5.根据块文件创建读对象,获的块文件放入字符缓冲区,并设置块文件长度len
 * 6.将字符缓冲区的块文件从0开始到len写入到合并文件中
 * 7.循环结束,合并块文件成功
 */
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值