当前位置: 首页 > news >正文

秦皇岛网站建公司淘宝关键词排名是怎么做的

秦皇岛网站建公司,淘宝关键词排名是怎么做的,企业网站前台模板,为什么做网站要用谷歌浏览器springbootvue大文件断点续传 后端代码代码说明1.上传分片接口2. 查询已上传分片接口3. 合并分片接口4.后端流程 前端组件前端流程 后端代码 RestController RequestMapping("/api/commonFileUtil") public class CommonFileController {Autowiredprivate FilePathC…

springboot+vue大文件断点续传

    • 后端代码
    • 代码说明
      • 1.上传分片接口
      • 2. 查询已上传分片接口
      • 3. 合并分片接口
      • 4.后端流程
    • 前端组件
      • 前端流程

后端代码

@RestController
@RequestMapping("/api/commonFileUtil")
public class CommonFileController {@Autowiredprivate FilePathConfig filePathConfig;/*** 上传分片**/@PostMapping("commonUploadByModule/{module}")public ActionResult<?> commonUploadByModule(@PathVariable("module") String folder,@RequestPart("file") MultipartFile multipartFile,@RequestParam("fileHash") String fileHash,@RequestParam("chunkIndex") int chunkIndex,@RequestParam("totalChunks") int totalChunks,@RequestParam("fileName") String originalFileName) throws IOException {String baseFolder = filePathConfig.getUploadByModule() + (StrUtil.isEmpty(folder) ? "default" : folder);String tempFolder = baseFolder + "/temp/" + fileHash;FileUtil.checkAndCreateFolder(tempFolder);File chunkFile = new File(tempFolder, chunkIndex + ".part");IoUtil.write(new FileOutputStream(chunkFile), true, multipartFile.getBytes());return ActionResult.success("分片上传成功");}/*** 查询自己的分片**/@GetMapping("commonUploadByModule/{module}/status")public ActionResult<?> uploadedChunks(@PathVariable("module") String folder,@RequestParam("fileHash") String fileHash) {String baseFolder = filePathConfig.getUploadByModule() + (StrUtil.isEmpty(folder) ? "default" : folder);String tempFolder = baseFolder + "/temp/" + fileHash;List<Integer> uploaded = new ArrayList<>();File folderFile = new File(tempFolder);if (folderFile.exists()) {File[] files = folderFile.listFiles((dir, name) -> name.endsWith(".part"));if (files != null) {for (File file : files) {String name = file.getName().replace(".part", "");uploaded.add(Integer.parseInt(name));}}}Map<Object, Object> map = new HashMap<>();map.put("uploaded", uploaded);return ActionResult.success(map);}/*** 分片合并**/@PostMapping("commonUploadByModule/{module}/merge")public ActionResult<?> mergeChunks(@PathVariable("module") String folder,@RequestBody Map<String, String> params) throws IOException {String fileHash = params.get("fileHash");String originalFileName = params.get("fileName");String baseFolder = filePathConfig.getUploadByModule() + (StrUtil.isEmpty(folder) ? "default" : folder);String tempFolder = baseFolder + "/temp/" + fileHash;File[] chunks = new File(tempFolder).listFiles((dir, name) -> name.endsWith(".part"));if (chunks == null || chunks.length == 0) {return ActionResult.fail("未找到分片文件");}Arrays.sort(chunks, Comparator.comparingInt(f -> Integer.parseInt(f.getName().replace(".part", ""))));String mergedFileName = FileUtil.addPrefix(Paths.get(originalFileName).getFileName().toString());String mergedFilePath = baseFolder + "/" + mergedFileName;FileUtil.checkAndCreateFolder(baseFolder);try (FileOutputStream fos = new FileOutputStream(mergedFilePath)) {for (File chunk : chunks) {Files.copy(chunk.toPath(), fos);}}// 清理临时文件夹for (File chunk : chunks) chunk.delete();new File(tempFolder).delete();Map<String, String> map = new HashMap<>();map.put("fileName", mergedFileName);map.put("fileOriginName", originalFileName);map.put("fileSize", String.format("%.2fMB", new File(mergedFilePath).length() / 1024 / 1024f));map.put("filePath", mergedFilePath);return ActionResult.success(map);}
}

代码说明

1.上传分片接口

@PostMapping("{module}")
public ActionResult<?> uploadChunk(@PathVariable("module") String folder,@RequestPart("file") MultipartFile multipartFile,@RequestParam("fileHash") String fileHash,@RequestParam("chunkIndex") int chunkIndex,@RequestParam("totalChunks") int totalChunks,@RequestParam("fileName") String originalFileName
) throws IOException {
  • @PostMapping(“{module}”): 表示 POST 请求路径为 /uploadByModule/{module},例如:/uploadByModule/video。

  • @PathVariable(“module”) String folder: 获取路径变量 module,如 “video”,表示上传分类。

  • @RequestPart(“file”) MultipartFile multipartFile: 接收上传的文件分片。

  • 其余参数为前端额外上传的信息:

    • fileHash: 前端根据文件内容生成的唯一标识;

    • chunkIndex: 当前是第几个分片;

    • totalChunks: 总共有多少个分片;

    • fileName: 文件原始名。

String baseFolder = filePathConfig.getUploadByModule() + (StrUtil.isEmpty(folder) ? "default" : folder);
  • 构造出完整的基础目录路径,比如:/mnt/upload/video。

  • 如果路径为空,则用默认目录 /mnt/upload/default。

String tempFolder = baseFolder + "/temp/" + fileHash;
FileUtil.checkAndCreateFolder(tempFolder);
  • 构造当前文件的临时目录,如 /mnt/upload/video/temp/abc123。

  • checkAndCreateFolder() 是你自己定义的工具方法,用于确保目录存在(不存在则创建)。

File chunkFile = new File(tempFolder, chunkIndex + ".part");
IoUtil.write(new FileOutputStream(chunkFile), true, multipartFile.getBytes());
  • 创建当前分片的目标文件对象,例如 /mnt/upload/video/temp/abc123/0.part。

  • 使用 IoUtil.write() 将上传的分片写入本地文件。

2. 查询已上传分片接口

@GetMapping("{module}/status")
public ActionResult<?> uploadedChunks(@PathVariable("module") String folder,@RequestParam("fileHash") String fileHash
) {
  • GET 请求路径为:/uploadByModule/video/status?fileHash=abc123

  • 返回指定文件(通过 fileHash 唯一标识)的 已上传分片编号。

String baseFolder = filePathConfig.getUploadByModule() + (StrUtil.isEmpty(folder) ? "default" : folder);
String tempFolder = baseFolder + "/temp/" + fileHash;
  • 计算临时分片文件夹路径。
List<Integer> uploaded = new ArrayList<>();
File folderFile = new File(tempFolder);
  • 准备存储已上传的分片编号。
if (folderFile.exists()) {File[] files = folderFile.listFiles((dir, name) -> name.endsWith(".part"));
  • 判断该文件夹是否存在,如果存在则读取里面所有以 .part 结尾的文件(即分片)。
    for (File f : files) {String name = f.getName().replace(".part", "");uploaded.add(Integer.parseInt(name));}
}
  • 去除每个文件名的 .part 后缀,并转为数字(分片编号)存入列表中。

3. 合并分片接口

@PostMapping("{module}/merge")
public ActionResult<?> mergeChunks(@PathVariable("module") String folder,@RequestBody Map<String, String> params
) throws IOException {
  • POST /uploadByModule/video/merge

  • @RequestBody 参数为 JSON 对象,包含两个 key:

  • fileHash: 文件唯一标识;

  • fileName: 原始文件名。

String fileHash = params.get("fileHash");
String originalFileName = params.get("fileName");
  • 从请求体中提取参数。
String baseFolder = filePathConfig.getUploadByModule() + (StrUtil.isEmpty(folder) ? "default" : folder);
String tempFolder = baseFolder + "/temp/" + fileHash;
  • 组装临时目录。
File[] chunks = new File(tempFolder).listFiles((dir, name) -> name.endsWith(".part"));
if (chunks == null || chunks.length == 0) {return ActionResult.error("未找到分片文件");
}
Arrays.sort(chunks, Comparator.comparingInt(f -> Integer.parseInt(f.getName().replace(".part", ""))));
  • 对所有分片文件按编号升序排序,确保按顺序合并。
String mergedFileName = FileUtil.addPrefix(Paths.get(originalFileName).getFileName().toString());
String mergedFilePath = baseFolder + "/" + mergedFileName;
FileUtil.checkAndCreateFolder(baseFolder);
  • 使用你的 addPrefix() 工具方法给原始文件名加上唯一前缀;

  • 最终合并后文件路径:如 /mnt/upload/video/1651283761234_myfile.mp4

try (FileOutputStream fos = new FileOutputStream(mergedFilePath)) {for (File chunk : chunks) {Files.copy(chunk.toPath(), fos);}
}
  • 创建输出流 fos,逐个读取 .part 文件并写入合并目标文件。
for (File chunk : chunks) chunk.delete();
new File(tempFolder).delete();
  • 合并完成后删除所有分片及临时文件夹。
Map<String, String> map = new HashMap<>();
map.put("fileName", mergedFileName);
map.put("fileOriginName", originalFileName);
map.put("fileSize", String.format("%.2fMB", new File(mergedFilePath).length() / 1024f / 1024f));
map.put("filePath", mergedFilePath);
return ActionResult.success(map);
  • 返回完整信息给前端,包括:

    • 实际文件名(带前缀);

    • 原始名称;

    • 文件大小;

    • 路径。

4.后端流程

[后端]

  • 每个分片保存在 temp/{fileHash}/chunkIndex.part
  • 合并时按照顺序将所有 part 合成文件

前端组件


<template><div class="upload-wrapper"><input type="file" @change="handleFileChange" /><button :disabled="!file || uploading" @click="uploadFile">{{ uploading ? '上传中...' : '上传文件' }}</button><div v-if="uploading">上传进度:{{ progress.toFixed(2) }}%</div></div>
</template><script>
import Vue from "vue"
import SparkMD5 from 'spark-md5'
import request from "@/utils/request";export default {data () {return {file: null,chunkSize: 5 * 1024 * 1024, // 每片 5MBuploading: false,progress: 0,module: 'video' // 上传模块路径,如 default、video、image...}},methods: {handleFileChange (e) {this.file = e.target.files[0]},async uploadFile () {if (!this.file) returnthis.uploading = trueconst fileHash = await this.calculateFileHash(this.file)const totalChunks = Math.ceil(this.file.size / this.chunkSize)const uploadedChunkIndexes = await this.getUploadedChunkIndexes(fileHash)for (let index = 0; index < totalChunks; index++) {if (uploadedChunkIndexes.includes(index)) {this.updateProgress(index + 1, totalChunks)continue}const start = index * this.chunkSizeconst end = Math.min(start + this.chunkSize, this.file.size)const chunk = this.file.slice(start, end)await this.uploadChunk({chunk,fileHash,chunkIndex: index,totalChunks,fileName: this.file.name})this.updateProgress(index + 1, totalChunks)}await this.mergeChunks(fileHash, this.file.name)this.uploading = falsealert('上传成功')},updateProgress (uploadedChunks, totalChunks) {this.progress = (uploadedChunks / totalChunks) * 100},async calculateFileHash (file) {return new Promise(resolve => {const spark = new SparkMD5.ArrayBuffer()const reader = new FileReader()const chunkSize = this.chunkSizeconst chunks = Math.ceil(file.size / chunkSize)let currentChunk = 0const loadNext = () => {const start = currentChunk * chunkSizeconst end = Math.min(start + chunkSize, file.size)reader.readAsArrayBuffer(file.slice(start, end))}reader.onload = e => {spark.append(e.target.result)currentChunk++if (currentChunk < chunks) {loadNext()} else {resolve(spark.end())}}loadNext()})},async getUploadedChunkIndexes (fileHash) {// try {//   const res = await axios.get(`/api/commonFileUtil/commonUploadByModule/${this.module}/status`, {//     params: { fileHash }//   })//   return res.data.data.uploaded || []// } catch (e) {//   return []// }return new Promise((resolve, reject) => {request({url: `/api/commonFileUtil/commonUploadByModule/${this.module}/status?fileHash=${fileHash}`,method: "GET"}).then((res) => {if (res && res.data && Array.isArray(res.data.uploaded)) {resolve(res.data.uploaded)} else {// 兜底,避免不是数组时报错resolve([])}}).catch((err) => {reject([])})})},async uploadChunk ({ chunk, fileHash, chunkIndex, totalChunks, fileName }) {const formData = new FormData()formData.append('file', chunk)formData.append('fileHash', fileHash)formData.append('chunkIndex', chunkIndex)formData.append('totalChunks', totalChunks)formData.append('fileName', fileName)return new Promise((resolve, reject) => {request({url: `/api/commonFileUtil/commonUploadByModule/${this.module}`,method: "post",data: formData,}).then((res) => {resolve(res.data)}).catch((err) => {reject(err)})})},async mergeChunks (fileHash, fileName) {return new Promise((resolve, reject) => {request({url: `/api/commonFileUtil/commonUploadByModule/${this.module}/merge`,method: "post",data: {fileHash,fileName},}).then((res) => {resolve(res.data)}).catch((err) => {reject(err)})})}}
}
</script><style scoped>
.upload-wrapper {padding: 20px;border: 1px dashed #999;border-radius: 10px;width: 400px;
}
</style>

前端流程

[前端]
→ 1. 计算 fileHash
→ 2. 查询 status 接口,获取已上传分片
→ 3. 上传分片(附带 index/hash/total)
→ 4. 上传完成后,调用 merge 接口

http://www.wooajung.com/news/27060.html

相关文章:

  • 墙绘做网站推广有作用没公司关键词seo
  • 装饰公司简介内容360搜索优化
  • 百度联盟怎么做网站加入新产品上市推广策划方案
  • 网站引导页分为三个板块设计风格seo实战密码第三版pdf下载
  • 做关于什么样的网站好seo网络优化招聘信息
  • 成都人高清影院品牌加盟上海网站排名seo公司哪家好
  • 河北省住房和建设厅网站网上营销方法
  • 唐山网站主页制作培训seo去哪家机构最好
  • 做公司网站哪家好网站点击量软件
  • 怎样做理财投资网站重庆seo优化推广
  • 哈尔滨网站建设一薇ls15227seo优化大公司排名
  • 设计家官网下载网站seo外包公司有哪些
  • 一个空间怎么做两个网站关键词seo价格
  • 湖南关键词优化品牌推荐网站优化怎么操作
  • 企业年金交了有好处吗seo网站排名优化工具
  • 外贸网站建设入门黑帽seo之搜索引擎
  • 深圳网站建设外包公司哪家好成人英语培训班哪个机构好
  • 文昌建设局网站搜索关键词推荐
  • 网站建设仟首先金手指12百度广告一级代理
  • 无锡做网站公司有哪些电话兔子bt樱桃搜索磁力天堂
  • 网站建设营销营销方案
  • 饰品做商城网站模式广告推广策划方案
  • 学院网站的作用网站自助建站系统
  • 买好域名和云主机后怎么做网站百度快照如何优化
  • 网站后台 生成所有页面进入百度网首页
  • 郑州有哪些做网站的公司免费seo优化工具
  • 企业网站的功能可分为前台和后台两个部分商品seo优化是什么意思
  • 广州网站制作后缀百度收录刷排名
  • 日本一级做a在线播放免费视频网站发布软文广告
  • 做旅游的网站的目的和意义网上怎么做推广