赞
踩
vue-simple-uploader是基于 simple-uploader.js 封装的vue上传插件。它的优点包括且不限于以下几种:
秒传:(将文件使用MD5加密,生成一个串,我们拿到这个串到redis 中查看是否存在)
- package com.xialj.demoend.controller;
-
- import com.xialj.demoend.common.RestApiResponse;
- import com.xialj.demoend.common.Result;
- import com.xialj.demoend.dto.FileChunkDTO;
- import com.xialj.demoend.dto.FileChunkResultDTO;
- import com.xialj.demoend.service.IUploadService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.*;
-
- import javax.annotation.Resource;
-
- /**
- * @ProjectName UploaderController
- * @author Administrator
- * @version 1.0.0
- * @Description 附件分片上传
- * @createTime 2022/4/13 0013 15:58
- */
- @RestController
- @RequestMapping("upload")
- public class UploaderController {
-
- @Resource
- private IUploadService uploadService;
-
- /**
- * 检查分片是否存在
- *
- * @return
- */
- @GetMapping("chunk")
- public Result checkChunkExist(FileChunkDTO chunkDTO) {
- FileChunkResultDTO fileChunkCheckDTO;
- try {
- fileChunkCheckDTO = uploadService.checkChunkExist(chunkDTO);
- return Result.ok(fileChunkCheckDTO);
- } catch (Exception e) {
- return Result.fail(e.getMessage());
- }
- }
-
-
- /**
- * 上传文件分片
- *
- * @param chunkDTO
- * @return
- */
- @PostMapping("chunk")
- public Result uploadChunk(FileChunkDTO chunkDTO) {
- try {
- uploadService.uploadChunk(chunkDTO);
- return Result.ok(chunkDTO.getIdentifier());
- } catch (Exception e) {
- return Result.fail(e.getMessage());
- }
- }
-
- /**
- * 请求合并文件分片
- *
- * @param chunkDTO
- * @return
- */
- @PostMapping("merge")
- public Result mergeChunks(@RequestBody FileChunkDTO chunkDTO) {
- try {
- boolean success = uploadService.mergeChunk(chunkDTO.getIdentifier(), chunkDTO.getFilename(), chunkDTO.getTotalChunks());
- return Result.ok(success);
- } catch (Exception e) {
- return Result.fail(e.getMessage());
- }
- }
-
- }
- package com.xialj.demoend.service;
- import com.xialj.demoend.dto.FileChunkDTO;
- import com.xialj.demoend.dto.FileChunkResultDTO;
-
- import java.io.IOException;
-
- /**
- * @ProjectName IUploadService
- * @author Administrator
- * @version 1.0.0
- * @Description 附件分片上传
- * @createTime 2022/4/13 0013 15:59
- */
- public interface IUploadService {
-
- /**
- * 检查文件是否存在,如果存在则跳过该文件的上传,如果不存在,返回需要上传的分片集合
- * @param chunkDTO
- * @return
- */
- FileChunkResultDTO checkChunkExist(FileChunkDTO chunkDTO);
-
-
- /**
- * 上传文件分片
- * @param chunkDTO
- */
- void uploadChunk(FileChunkDTO chunkDTO) throws IOException;
-
-
- /**
- * 合并文件分片
- * @param identifier
- * @param fileName
- * @param totalChunks
- * @return
- * @throws IOException
- */
- boolean mergeChunk(String identifier,String fileName,Integer totalChunks)throws IOException;
- }
- package com.xialj.demoend.service.impl;
- import com.xialj.demoend.dto.FileChunkDTO;
- import com.xialj.demoend.dto.FileChunkResultDTO;
- import com.xialj.demoend.service.IUploadService;
- import org.apache.tomcat.util.http.fileupload.IOUtils;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.stereotype.Service;
-
- import java.io.*;
- import java.util.*;
-
-
- /**
- * @ProjectName UploadServiceImpl
- * @author Administrator
- * @version 1.0.0
- * @Description 附件分片上传
- * @createTime 2022/4/13 0013 15:59
- */
- @Service
- @SuppressWarnings("all")
- public class UploadServiceImpl implements IUploadService {
-
- private Logger logger = LoggerFactory.getLogger(UploadServiceImpl.class);
-
- @Autowired
- private RedisTemplate<String, Object> redisTemplate;
-
- @Value("${uploadFolder}")
- private String uploadFolder;
-
- /**
- * 检查文件是否存在,如果存在则跳过该文件的上传,如果不存在,返回需要上传的分片集合
- * 检查分片是否存在
- ○ 检查目录下的文件是否存在。
- ○ 检查redis存储的分片是否存在。
- ○ 判断分片数量和总分片数量是否一致。
- 如果文件存在并且分片上传完毕,标识已经完成附件的上传,可以进行秒传操作。
- 如果文件不存在或者分片为上传完毕,则返回false并返回已经上传的分片信息。
- * @param chunkDTO
- * @return
- */
- @Override
- public FileChunkResultDTO checkChunkExist(FileChunkDTO chunkDTO) {
- //1.检查文件是否已上传过
- //1.1)检查在磁盘中是否存在
- String fileFolderPath = getFileFolderPath(chunkDTO.getIdentifier());
- logger.info("fileFolderPath-->{}", fileFolderPath);
- String filePath = getFilePath(chunkDTO.getIdentifier(), chunkDTO.getFilename());
- File file = new File(filePath);
- boolean exists = file.exists();
- //1.2)检查Redis中是否存在,并且所有分片已经上传完成。
- Set<Integer> uploaded = (Set<Integer>) redisTemplate.opsForHash().get(chunkDTO.getIdentifier(), "uploaded");
- if (uploaded != null && uploaded.size() == chunkDTO.getTotalChunks() && exists) {
- return new FileChunkResultDTO(true);
- }
- File fileFolder = new File(fileFolderPath);
- if (!fileFolder.exists()) {
- boolean mkdirs = fileFolder.mkdirs();
- logger.info("准备工作,创建文件夹,fileFolderPath:{},mkdirs:{}", fileFolderPath, mkdirs);
- }
- // 断点续传,返回已上传的分片
- return new FileChunkResultDTO(false, uploaded);
- }
-
-
- /**
- * 上传分片
- * 上传附件分片
- ○ 判断目录是否存在,如果不存在则创建目录。
- ○ 进行切片的拷贝,将切片拷贝到指定的目录。
- ○ 将该分片写入redis
- * @param chunkDTO
- */
- @Override
- public void uploadChunk(FileChunkDTO chunkDTO) {
- //分块的目录
- String chunkFileFolderPath = getChunkFileFolderPath(chunkDTO.getIdentifier());
- logger.info("分块的目录 -> {}", chunkFileFolderPath);
- File chunkFileFolder = new File(chunkFileFolderPath);
- if (!chunkFileFolder.exists()) {
- boolean mkdirs = chunkFileFolder.mkdirs();
- logger.info("创建分片文件夹:{}", mkdirs);
- }
- //写入分片
- try (
- InputStream inputStream = chunkDTO.getFile().getInputStream();
- FileOutputStream outputStream = new FileOutputStream(new File(chunkFileFolderPath + chunkDTO.getChunkNumber()))
- ) {
- IOUtils.copy(inputStream, outputStream);
- logger.info("文件标识:{},chunkNumber:{}", chunkDTO.getIdentifier(), chunkDTO.getChunkNumber());
- //将该分片写入redis
- long size = saveToRedis(chunkDTO);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
-
- @Override
- public boolean mergeChunk(String identifier, String fileName, Integer totalChunks) throws IOException {
- return mergeChunks(identifier, fileName, totalChunks);
- }
-
- /**
- * 合并分片
- *
- * @param identifier
- * @param filename
- */
- private boolean mergeChunks(String identifier, String filename, Integer totalChunks) {
- String chunkFileFolderPath = getChunkFileFolderPath(identifier);
- String filePath = getFilePath(identifier, filename);
- // 检查分片是否都存在
- if (checkChunks(chunkFileFolderPath, totalChunks)) {
- File chunkFileFolder = new File(chunkFileFolderPath);
- File mergeFile = new File(filePath);
- File[] chunks = chunkFileFolder.listFiles();
- // 切片排序1、2/3、---
- List fileList = Arrays.asList(chunks);
- Collections.sort(fileList, (Comparator<File>) (o1, o2) -> {
- return Integer.parseInt(o1.getName()) - (Integer.parseInt(o2.getName()));
- });
- try {
- RandomAccessFile randomAccessFileWriter = new RandomAccessFile(mergeFile, "rw");
- byte[] bytes = new byte[1024];
- for (File chunk : chunks) {
- RandomAccessFile randomAccessFileReader = new RandomAccessFile(chunk, "r");
- int len;
- while ((len = randomAccessFileReader.read(bytes)) != -1) {
- randomAccessFileWriter.write(bytes, 0, len);
- }
- randomAccessFileReader.close();
- }
- randomAccessFileWriter.close();
- } catch (Exception e) {
- return false;
- }
- return true;
- }
- return false;
- }
-
- /**
- * 检查分片是否都存在
- * @param chunkFileFolderPath
- * @param totalChunks
- * @return
- */
- private boolean checkChunks(String chunkFileFolderPath, Integer totalChunks) {
- try {
- for (int i = 1; i <= totalChunks + 1; i++) {
- File file = new File(chunkFileFolderPath + File.separator + i);
- if (file.exists()) {
- continue;
- } else {
- return false;
- }
- }
- } catch (Exception e) {
- return false;
- }
- return true;
- }
-
- /**
- * 分片写入Redis
- * 判断切片是否已存在,如果未存在,则创建基础信息,并保存。
- * @param chunkDTO
- */
- private synchronized long saveToRedis(FileChunkDTO chunkDTO) {
- Set<Integer> uploaded = (Set<Integer>) redisTemplate.opsForHash().get(chunkDTO.getIdentifier(), "uploaded");
- if (uploaded == null) {
- uploaded = new HashSet<>(Arrays.asList(chunkDTO.getChunkNumber()));
- HashMap<String, Object> objectObjectHashMap = new HashMap<>();
- objectObjectHashMap.put("uploaded", uploaded);
- objectObjectHashMap.put("totalChunks", chunkDTO.getTotalChunks());
- objectObjectHashMap.put("totalSize", chunkDTO.getTotalSize());
- // objectObjectHashMap.put("path", getFileRelativelyPath(chunkDTO.getIdentifier(), chunkDTO.getFilename()));
- objectObjectHashMap.put("path", chunkDTO.getFilename());
- redisTemplate.opsForHash().putAll(chunkDTO.getIdentifier(), objectObjectHashMap);
- } else {
- uploaded.add(chunkDTO.getChunkNumber());
- redisTemplate.opsForHash().put(chunkDTO.getIdentifier(), "uploaded", uploaded);
- }
- return uploaded.size();
- }
-
- /**
- * 得到文件的绝对路径
- *
- * @param identifier
- * @param filename
- * @return
- */
- private String getFilePath(String identifier, String filename) {
- String ext = filename.substring(filename.lastIndexOf("."));
- // return getFileFolderPath(identifier) + identifier + ext;
- return uploadFolder + filename;
- }
-
- /**
- * 得到文件的相对路径
- *
- * @param identifier
- * @param filename
- * @return
- */
- private String getFileRelativelyPath(String identifier, String filename) {
- String ext = filename.substring(filename.lastIndexOf("."));
- return "/" + identifier.substring(0, 1) + "/" +
- identifier.substring(1, 2) + "/" +
- identifier + "/" + identifier
- + ext;
- }
-
-
- /**
- * 得到分块文件所属的目录
- *
- * @param identifier
- * @return
- */
- private String getChunkFileFolderPath(String identifier) {
- return getFileFolderPath(identifier) + "chunks" + File.separator;
- }
-
- /**
- * 得到文件所属的目录
- *
- * @param identifier
- * @return
- */
- private String getFileFolderPath(String identifier) {
- return uploadFolder + identifier.substring(0, 1) + File.separator +
- identifier.substring(1, 2) + File.separator +
- identifier + File.separator;
- // return uploadFolder;
- }
- }
- package com.xialj.demoend.dto;
- import org.springframework.web.multipart.MultipartFile;
-
- /**
- * @ProjectName FileChunkDTO
- * @author Administrator
- * @version 1.0.0
- * @Description 附件分片上传
- * @createTime 2022/4/13 0013 15:59
- */
- public class FileChunkDTO {
- /**
- * 文件 md5
- */
- private String identifier;
- /**
- * 分块文件
- */
- MultipartFile file;
- /**
- * 当前分块序号
- */
- private Integer chunkNumber;
- /**
- * 分块大小
- */
- private Long chunkSize;
- /**
- * 当前分块大小
- */
- private Long currentChunkSize;
- /**
- * 文件总大小
- */
- private Long totalSize;
- /**
- * 分块总数
- */
- private Integer totalChunks;
- /**
- * 文件名
- */
- private String filename;
-
- public String getIdentifier() {
- return identifier;
- }
-
- public void setIdentifier(String identifier) {
- this.identifier = identifier;
- }
-
- public MultipartFile getFile() {
- return file;
- }
-
- public void setFile(MultipartFile file) {
- this.file = file;
- }
-
-
- public Integer getChunkNumber() {
- return chunkNumber;
- }
-
- public void setChunkNumber(Integer chunkNumber) {
- this.chunkNumber = chunkNumber;
- }
-
- public Long getChunkSize() {
- return chunkSize;
- }
-
- public void setChunkSize(Long chunkSize) {
- this.chunkSize = chunkSize;
- }
-
- public Long getCurrentChunkSize() {
- return currentChunkSize;
- }
-
- public void setCurrentChunkSize(Long currentChunkSize) {
- this.currentChunkSize = currentChunkSize;
- }
-
- public Long getTotalSize() {
- return totalSize;
- }
-
- public void setTotalSize(Long totalSize) {
- this.totalSize = totalSize;
- }
-
- public Integer getTotalChunks() {
- return totalChunks;
- }
-
- public void setTotalChunks(Integer totalChunks) {
- this.totalChunks = totalChunks;
- }
-
- public String getFilename() {
- return filename;
- }
-
- public void setFilename(String filename) {
- this.filename = filename;
- }
-
- @Override
- public String toString() {
- return "FileChunkDTO{" +
- "identifier='" + identifier + '\'' +
- ", file=" + file +
- ", chunkNumber=" + chunkNumber +
- ", chunkSize=" + chunkSize +
- ", currentChunkSize=" + currentChunkSize +
- ", totalSize=" + totalSize +
- ", totalChunks=" + totalChunks +
- ", filename='" + filename + '\'' +
- '}';
- }
- }
- package com.xialj.demoend.dto;
-
-
- import java.util.Set;
-
- /**
- * @ProjectName FileChunkResultDTO
- * @author Administrator
- * @version 1.0.0
- * @Description 附件分片上传
- * @createTime 2022/4/13 0013 15:59
- */
- public class FileChunkResultDTO {
- /**
- * 是否跳过上传
- */
- private Boolean skipUpload;
-
- /**
- * 已上传分片的集合
- */
- private Set<Integer> uploaded;
-
- public Boolean getSkipUpload() {
- return skipUpload;
- }
-
- public void setSkipUpload(Boolean skipUpload) {
- this.skipUpload = skipUpload;
- }
-
- public Set<Integer> getUploaded() {
- return uploaded;
- }
-
- public void setUploaded(Set<Integer> uploaded) {
- this.uploaded = uploaded;
- }
-
-
- public FileChunkResultDTO(Boolean skipUpload, Set<Integer> uploaded) {
- this.skipUpload = skipUpload;
- this.uploaded = uploaded;
- }
-
- public FileChunkResultDTO(Boolean skipUpload) {
- this.skipUpload = skipUpload;
- }
- }
- package com.xialj.demoend.common;
-
- import io.swagger.annotations.ApiModel;
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
- /**
- * @Author
- * @Date Created in 2023/2/23 17:25
- * @DESCRIPTION: 全局统一返回结果
- * @Version V1.0
- */
- @Data
- @ApiModel(value = "全局统一返回结果")
- @SuppressWarnings("all")
- public class Result<T> {
-
- @ApiModelProperty(value = "返回码")
- private Integer code;
-
- @ApiModelProperty(value = "返回消息")
- private String message;
-
- @ApiModelProperty(value = "返回数据")
- private T data;
- private Long total;
-
- public Result(){}
-
- protected static <T> Result<T> build(T data) {
- Result<T> result = new Result<T>();
- if (data != null)
- result.setData(data);
- return result;
- }
-
- public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {
- Result<T> result = build(body);
- result.setCode(resultCodeEnum.getCode());
- result.setMessage(resultCodeEnum.getMessage());
- return result;
- }
-
- public static <T> Result<T> build(Integer code, String message) {
- Result<T> result = build(null);
- result.setCode(code);
- result.setMessage(message);
- return result;
- }
-
- public static<T> Result<T> ok(){
- return Result.ok(null);
- }
-
- /**
- * 操作成功
- * @param data
- * @param <T>
- * @return
- */
- public static<T> Result<T> ok(T data){
- Result<T> result = build(data);
- return build(data, ResultCodeEnum.SUCCESS);
- }
-
- public static<T> Result<T> fail(){
- return Result.fail(null);
- }
-
- /**
- * 操作失败
- * @param data
- * @param <T>
- * @return
- */
- public static<T> Result<T> fail(T data){
- Result<T> result = build(data);
- return build(data, ResultCodeEnum.FAIL);
- }
-
- public Result<T> message(String msg){
- this.setMessage(msg);
- return this;
- }
-
- public Result<T> code(Integer code){
- this.setCode(code);
- return this;
- }
-
- public boolean isOk() {
- if(this.getCode().intValue() == ResultCodeEnum.SUCCESS.getCode().intValue()) {
- return true;
- }
- return false;
- }
- }
- package com.xialj.demoend.common;
-
- import lombok.Getter;
-
- /**
- * @Author
- * @Date Created in 2023/2/23 17:25
- * @DESCRIPTION: 统一返回结果状态信息类
- * @Version V1.0
- */
- @Getter
- @SuppressWarnings("all")
- public enum ResultCodeEnum {
-
- SUCCESS(200,"成功"),
- FAIL(201, "失败"),
- PARAM_ERROR( 202, "参数不正确"),
- SERVICE_ERROR(203, "服务异常"),
- DATA_ERROR(204, "数据异常"),
- DATA_UPDATE_ERROR(205, "数据版本异常"),
-
- LOGIN_AUTH(208, "未登陆"),
- PERMISSION(209, "没有权限"),
-
- CODE_ERROR(210, "验证码错误"),
- // LOGIN_MOBLE_ERROR(211, "账号不正确"),
- LOGIN_DISABLED_ERROR(212, "改用户已被禁用"),
- REGISTER_MOBLE_ERROR(213, "手机号码格式不正确"),
- REGISTER_MOBLE_ERROR_NULL(214, "手机号码为空"),
-
- LOGIN_AURH(214, "需要登录"),
- LOGIN_ACL(215, "没有权限"),
-
- URL_ENCODE_ERROR( 216, "URL编码失败"),
- ILLEGAL_CALLBACK_REQUEST_ERROR( 217, "非法回调请求"),
- FETCH_ACCESSTOKEN_FAILD( 218, "获取accessToken失败"),
- FETCH_USERINFO_ERROR( 219, "获取用户信息失败"),
- //LOGIN_ERROR( 23005, "登录失败"),
-
- PAY_RUN(220, "支付中"),
- CANCEL_ORDER_FAIL(225, "取消订单失败"),
- CANCEL_ORDER_NO(225, "不能取消预约"),
-
- HOSCODE_EXIST(230, "医院编号已经存在"),
- NUMBER_NO(240, "可预约号不足"),
- TIME_NO(250, "当前时间不可以预约"),
-
- SIGN_ERROR(300, "签名错误"),
- HOSPITAL_OPEN(310, "医院未开通,暂时不能访问"),
- HOSPITAL_LOCK(320, "医院被锁定,暂时不能访问"),
- HOSPITAL_LOCKKEY(330,"医院对应key不一致")
- ;
-
- private Integer code;
- private String message;
-
- private ResultCodeEnum(Integer code, String message) {
- this.code = code;
- this.message = message;
- }
- }
- npm install --save vue-simple-uploader
- npm install --save spark-md5
- import uploader from 'vue-simple-uploader'
- Vue.use(uploader)
- <template>
- <div>
- <uploader
- :autoStart="false"
- :options="options"
- :file-status-text="statusText"
- class="uploader-example"
- @file-complete="fileComplete"
- @complete="complete"
- @file-success="fileSuccess"
- @files-added="filesAdded"
- >
- <uploader-unsupport></uploader-unsupport>
- <uploader-drop>
- <p>将文件拖放到此处以上传</p>
- <uploader-btn>选择文件</uploader-btn>
- <uploader-btn :attrs="attrs">选择图片</uploader-btn>
- <uploader-btn :directory="true">选择文件夹</uploader-btn>
- </uploader-drop>
- <!-- <uploader-list></uploader-list> -->
- <uploader-files> </uploader-files>
- </uploader>
- <br />
- <el-button @click="allStart()" :disabled="disabled">全部开始</el-button>
- <el-button @click="allStop()" style="margin-left: 4px">全部暂停</el-button>
- <el-button @click="allRemove()" style="margin-left: 4px">全部移除</el-button>
- </div>
- </template>
-
- <script>
- import axios from "axios";
- import SparkMD5 from "spark-md5";
- import {upload} from "@/api/user";
- // import storage from "store";
- // import { ACCESS_TOKEN } from '@/store/mutation-types'
- export default {
- name: "Home",
- data() {
- return {
- skip: false,
- options: {
- target: "//localhost:9999/upload/chunk",
- // 开启服务端分片校验功能
- testChunks: true,
- parseTimeRemaining: function (timeRemaining, parsedTimeRemaining) {
- return parsedTimeRemaining
- .replace(/\syears?/, "年")
- .replace(/\days?/, "天")
- .replace(/\shours?/, "小时")
- .replace(/\sminutes?/, "分钟")
- .replace(/\sseconds?/, "秒");
- },
- // 服务器分片校验函数
- checkChunkUploadedByResponse: (chunk, message) => {
- const result = JSON.parse(message);
- if (result.data.skipUpload) {
- this.skip = true;
- return true;
- }
- return (result.data.uploaded || []).indexOf(chunk.offset + 1) >= 0;
- },
- // headers: {
- // // 在header中添加的验证,请根据实际业务来
- // "Access-Token": storage.get(ACCESS_TOKEN),
- // },
- },
- attrs: {
- accept: "image/*",
- },
- statusText: {
- success: "上传成功",
- error: "上传出错了",
- uploading: "上传中...",
- paused: "暂停中...",
- waiting: "等待中...",
- cmd5: "计算文件MD5中...",
- },
- fileList: [],
- disabled: true,
- };
- },
- watch: {
- fileList(o, n) {
- this.disabled = false;
- },
- },
- methods: {
- // fileSuccess(rootFile, file, response, chunk) {
- // // console.log(rootFile);
- // // console.log(file);
- // // console.log(message);
- // // console.log(chunk);
- // const result = JSON.parse(response);
- // console.log(result.success, this.skip);
- //
- // if (result.success && !this.skip) {
- // axios
- // .post(
- // "http://127.0.0.1:9999/upload/merge",
- // {
- // identifier: file.uniqueIdentifier,
- // filename: file.name,
- // totalChunks: chunk.offset,
- // },
- // // {
- // // headers: { "Access-Token": storage.get(ACCESS_TOKEN) }
- // // }
- // )
- // .then((res) => {
- // if (res.data.success) {
- // console.log("上传成功");
- // } else {
- // console.log(res);
- // }
- // })
- // .catch(function (error) {
- // console.log(error);
- // });
- // } else {
- // console.log("上传成功,不需要合并");
- // }
- // if (this.skip) {
- // this.skip = false;
- // }
- // },
- fileSuccess(rootFile, file, response, chunk) {
- // console.log(rootFile);
- // console.log(file);
- // console.log(message);
- // console.log(chunk);
- const result = JSON.parse(response);
- console.log(result.success, this.skip);
- const user = {
- identifier: file.uniqueIdentifier,
- filename: file.name,
- totalChunks: chunk.offset,
- }
- if (result.success && !this.skip) {
- upload(user).then((res) => {
- if (res.code == 200) {
- console.log("上传成功");
- } else {
- console.log(res);
- }
- })
- .catch(function (error) {
- console.log(error);
- });
- } else {
- console.log("上传成功,不需要合并");
- }
- if (this.skip) {
- this.skip = false;
- }
- },
- fileComplete(rootFile) {
- // 一个根文件(文件夹)成功上传完成。
- // console.log("fileComplete", rootFile);
- // console.log("一个根文件(文件夹)成功上传完成。");
- },
- complete() {
- // 上传完毕。
- // console.log("complete");
- },
- filesAdded(file, fileList, event) {
- // console.log(file);
- file.forEach((e) => {
- this.fileList.push(e);
- this.computeMD5(e);
- });
- },
- computeMD5(file) {
- let fileReader = new FileReader();
- let time = new Date().getTime();
- let blobSlice =
- File.prototype.slice ||
- File.prototype.mozSlice ||
- File.prototype.webkitSlice;
- let currentChunk = 0;
- const chunkSize = 1024 * 1024;
- let chunks = Math.ceil(file.size / chunkSize);
- let spark = new SparkMD5.ArrayBuffer();
- // 文件状态设为"计算MD5"
- file.cmd5 = true; //文件状态为“计算md5...”
- file.pause();
- loadNext();
- fileReader.onload = (e) => {
- spark.append(e.target.result);
- if (currentChunk < chunks) {
- currentChunk++;
- loadNext();
- // 实时展示MD5的计算进度
- console.log(
- `第${currentChunk}分片解析完成, 开始第${
- currentChunk + 1
- } / ${chunks}分片解析`
- );
- } else {
- let md5 = spark.end();
- console.log(
- `MD5计算完毕:${file.name} \nMD5:${md5} \n分片:${chunks} 大小:${
- file.size
- } 用时:${new Date().getTime() - time} ms`
- );
- spark.destroy(); //释放缓存
- file.uniqueIdentifier = md5; //将文件md5赋值给文件唯一标识
- file.cmd5 = false; //取消计算md5状态
- file.resume(); //开始上传
- }
- };
- fileReader.onerror = function () {
- this.error(`文件${file.name}读取出错,请检查该文件`);
- file.cancel();
- };
- function loadNext() {
- let start = currentChunk * chunkSize;
- let end =
- start + chunkSize >= file.size ? file.size : start + chunkSize;
- fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
- }
- },
- allStart() {
- console.log(this.fileList);
- this.fileList.map((e) => {
- if (e.paused) {
- e.resume();
- }
- });
- },
- allStop() {
- console.log(this.fileList);
- this.fileList.map((e) => {
- if (!e.paused) {
- e.pause();
- }
- });
- },
- allRemove() {
- this.fileList.map((e) => {
- e.cancel();
- });
- this.fileList = [];
- },
- },
- };
- </script>
-
- <style>
- .uploader-example {
- width: 100%;
- padding: 15px;
- margin: 0px auto 0;
- font-size: 12px;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
- }
- .uploader-example .uploader-btn {
- margin-right: 4px;
- }
- .uploader-example .uploader-list {
- max-height: 440px;
- overflow: auto;
- overflow-x: hidden;
- overflow-y: auto;
- }
- </style>
效果:
redis 中的文件:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。