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

转做海外买手的网站天天seo站长工具

转做海外买手的网站,天天seo站长工具,有高并发量门户网站开发经验,网站运营需要 做哪些工作文章目录 一、简化演示分块上传、合并分块断点续传秒传 二、更详细的逻辑和细节问题可能存在的隐患 三、代码示例前端代码后端代码 一、简化演示 分块上传、合并分块 前端将完整的视频文件分割成多份文件块,依次上传到后端,后端将其保存到文件系统。前…

文章目录

  • 一、简化演示
      • 分块上传、合并分块
      • 断点续传
      • 秒传
  • 二、更详细的逻辑和细节问题
      • 可能存在的隐患
  • 三、代码示例
      • 前端代码
      • 后端代码

一、简化演示

分块上传、合并分块

前端将完整的视频文件分割成多份文件块,依次上传到后端,后端将其保存到文件系统。前端将文件块上传完毕后,发送合并请求,后端拿取文件块,合并后重新上传到文件系统。
在这里插入图片描述

断点续传

前端遍历文件块,每次上传之前,先询问文件块是否存在,只有不存在的情况下,才会上传。
请添加图片描述

秒传

前端分割视频文件前,先询问此视频是否已经存在,存在则不再上传,后端之间返回视频信息。前端看起来就像是被秒传了。
请添加图片描述

二、更详细的逻辑和细节问题

  • 视频文件和文件块都通过文件本身计算MD5值作为唯一标志
  • 文件系统使用Minio,只要提供buckerNamepath就可以操作文件
  • 后端合并文件块成功后会删除文件块,并以MD5值为id存入数据库
  • Minio存储文件块时,依据其md5值计算path,比如取前两个字符构建二级文件夹,文件名为md5值,无后缀。所以只需要提供文件块的md5值就可以操作文件块。
  • Minio存储完整视频文件时,依据其md5值计算path,同上,文件名为md5值,携带.mp4等后缀,所以只需要提供视频文件的md5值就可以操作视频文件。
  1. 首先,前端计算视频文件的MD5值,记为fileMd5,传递MD5值来询问后端此视频文件是否存在,后端查询数据库返回结果,如果存在,则前端触发“秒传”。
  2. 如果不存在,则将视频文件分割成文件块,循环上传,每次循环,首先计算文件块的md5值,传递md5值询问后端此文件块是否存在,后端根据md5判断文件块是否存在,如果存在,前端跳过此文件块上传,直接标记为上传成功,如果不存在,则上传至后端,后端将其保存到minio。这其实就是“分块上传,断点续传”。
  3. 最后所有分块文件都上传成功,前端发起合并请求,传递视频文件的md5值和所有文件块的md5值到后端,后端进行文件块合并、文件块的删除、合并文件的上传,将信息存储在mysql数据库,将执行结果告知前端。这就是“合并分块”

可能存在的隐患

一个视频文件的文件块没有全部上传完成就终止,此时文件块将一直保存在minio中,如果之后此视频再也没有发起过上传请求,那么这些文件块都是是一种垃圾。

可以写一个定时任务,遍历Minio没有后缀的文件块,判断其创建时间距离当前是否足够久,是则删除。

三、代码示例

前端代码

<template><div class="p-2"><el-button icon="Plus" plain type="primary" @click="handleAdd">新增</el-button><!-- 添加或修改media对话框 --><el-dialog v-model="dialog.visible" :title="dialog.title" append-to-body width="500px"><el-form ref="mediaFormRef" :model="form" :rules="rules" label-width="80px"><el-form-item label="上传视频" prop="originalName" v-show="dialog.title=='添加视频'"><el-uploadref="uploadRef":http-request="onUpload":before-upload="beforeUpload":limit="1"action="#"class="upload-demo"><template #trigger><el-button type="primary">选择视频</el-button></template><template #tip><div class="el-upload__tip">支持分块上传、端点续传</div></template></el-upload></el-form-item><el-form-item v-show="percentageShow"><el-progress :percentage="percentage" style="width: 100%"/></el-form-item></el-form></el-dialog></div>
</template><script lang="ts" name="Media" setup>
import type {UploadInstance, UploadRawFile, UploadRequestOptions, UploadUserFile} from 'element-plus'
import SparkMD5 from "spark-md5";
import {HttpStatus} from "@/enums/RespEnum";const dialog = reactive<DialogOption>({visible: false,title: ''
});
//上传视频
const baseUrl = import.meta.env.VITE_APP_BASE_API;
const uploadImgUrl = ref(baseUrl + "/media/media/image"); // 上传的图片服务器地址
const uploadRef = ref<UploadInstance>()
const needUpload = ref(true)
const chunkSize = 5*1024*1024;const percentage = ref(0)
const percentageShow = ref(false)/** 新增按钮操作 */
const handleAdd = () => {dialog.visible = true;dialog.title = "添加视频";percentageShow.value = false;
}//获取文件的MD5
const getFileMd5 = (file:any) => {return new Promise((resolve, reject) => {let fileReader = new FileReader()fileReader.onload = function (event) {let fileMd5 = SparkMD5.ArrayBuffer.hash(event.target.result)resolve(fileMd5)}fileReader.readAsArrayBuffer(file)})
}//在上传之前,使用视频md5判断视频是否已经存在
const beforeUpload = async (rawFile: UploadRawFile) => {needUpload.value = true;const fileMd5 = await getFileMd5(rawFile);form.value.id = fileMd5;const rsp = await getMedia(fileMd5);if(!!rsp.data && rsp.data['id'] == fileMd5){needUpload.value = false;proxy?.$modal.msgWarning("视频文件已存在,请勿重复上传。文件名为"+rsp.data['originalName'])}
}//分块上传、合并分块
const onUpload = async (options: UploadRequestOptions) => {if(!needUpload.value){//秒传percentageShow.value = true;percentage.value = 100;dialog.visible = false;return;}percentageShow.value = true;const file = options.fileconst totalChunks = Math.ceil(file.size / chunkSize);let isUploadSuccess = true;//记录分块文件是否上传成功//合并文件参数let mergeVo = {"chunksMd5": [] as string[],"videoMd5": undefined as string | undefined,"videoName": file.name,"videoSize": file.size,"remark": undefined as string | undefined}//循环切分文件,并上传分块文件for(let i=0; i<totalChunks; ++i){const start = i * chunkSize;const end = Math.min(start + chunkSize, file.size);const chunk = file.slice(start, end);//计算 chunk md5const md5 = await getFileMd5(chunk);mergeVo.chunksMd5.push(md5);// 准备FormDataconst formData = new FormData();formData.append('file', chunk);formData.append('filename', file.name);formData.append('chunkIndex', i.toString());formData.append('totalChunks', totalChunks.toString());formData.append('md5', md5);//上传当前分块try {//先判断这个分块是否已经存在const isExistRsp = await isChunkExist({"md5": formData.get("md5")});const isExist = isExistRsp.data;//不存在则上传if (!isExist){const rsp = await addChunk(formData);console.log(`Chunk ${i + 1}/${totalChunks} uploaded`, rsp.data);}else {console.log(`Chunk ${i + 1}/${totalChunks} is exist`);}percentage.value = (i)*100 / totalChunks;} catch (error) {isUploadSuccess = false;console.error(`Error uploading chunk ${i + 1}`, error);proxy?.$modal.msgError(`上传分块${i + 1}出错`);break;}}//合并分块文件if(isUploadSuccess){proxy?.$modal.msgSuccess("分块文件上传成功")mergeVo.videoMd5 = form.value.id;//beforeUpload已经计算过视频文件的md5//合并文件const rsp = await mergeChunks(mergeVo);if (rsp.code == HttpStatus.SUCCESS){//合并文件后,实际上媒资已经插入数据库。percentage.value = 100;proxy?.$modal.msgSuccess("文件合并成功")proxy?.$modal.msgSuccess("视频上传成功")}else{proxy?.$modal.msgSuccess("文件合并异常")}}else {proxy?.$modal.msgSuccess("文件未上传成功,请重试或联系管理员")}
}</script>
export const getMedia = (id: string | number): AxiosPromise<MediaVO> => {return request({url: '/media/media/' + id,method: 'get'});
};/*** 分块文件是否存在* */
export const isChunkExist = (data: any) => {return request({url: '/media/media/video/chunk',method: 'get',params: data});
};/*** 上传分块文件* */
export const addChunk = (data: any) => {return request({url: '/media/media/video/chunk',method: 'post',data: data});
};/*** 合并分块文件* */
export const mergeChunks = (data: any) => {return request({url: '/media/media/video/chunk/merge',method: 'post',data: data});
};

后端代码

@RestController
@RequestMapping("/media")
public class MediaFilesController extends BaseController {/*** 获取media详细信息** @param id 主键*/@GetMapping("/{id}")public R<MediaFilesVo> getInfo(@NotNull(message = "主键不能为空")@PathVariable String id) {return R.ok(mediaFilesService.queryById(id));}@Log(title = "视频分块文件上传")@PostMapping(value = "/video/chunk")public R<String> handleChunkUpload(@RequestParam("file") MultipartFile file,@RequestParam("md5") String md5,@RequestParam("filename") String filename,@RequestParam("chunkIndex") int chunkIndex,@RequestParam("totalChunks") int totalChunks) {if (ObjectUtil.isNull(file)) {return R.fail("上传文件不能为空");}Boolean b = mediaFilesService.handleChunkUpload(file, md5);if (b){return R.ok();}else {return R.fail();}}@Log(title = "分块文件是否已经存在")@GetMapping(value = "/video/chunk")public R<Boolean> isChunkExist(@RequestParam("md5") String md5) {return R.ok(mediaFilesService.isChunkExist(md5));}@Log(title = "合并视频文件")@PostMapping(value = "/video/chunk/merge")public R<Boolean> mergeChunks(@RequestBody MediaVideoMergeBo bo) {bo.setCompanyId(LoginHelper.getDeptId());Boolean b = mediaFilesService.mergeChunks(bo);if (b){return R.ok();}else {return R.fail();}}
}

关于如何操作Minio等文件系统,不详细写明解释。只需要知道,给Minio提供文件本身、bucketName、path即可完成上传、下载、删除等操作。具体代码不同的包都不一样。

@Service
public class MediaFilesServiceImpl implements MediaFilesService {@Autowiredprivate MediaFilesMapper mediaFilesMapper;/*** 分块文件上传* <br/>* 分块文件不存放mysql信息,同时文件名不含后缀,只有md5* @param file 文件* @param md5  md5* @return {@link Boolean}*/@Overridepublic Boolean handleChunkUpload(MultipartFile file, String md5) {//只上传至minioOssClient storage = OssFactory.instance();String path = getPathByMD5(md5, "");try {storage.upload(file.getInputStream(), path, file.getContentType(), minioProperties.getVideoBucket());} catch (IOException e) {throw new RuntimeException(e);}return true;}@Overridepublic Boolean isChunkExist(String md5) {OssClient storage = OssFactory.instance();String path = getPathByMD5(md5, "");return storage.doesFileExist(minioProperties.getVideoBucket(), path);}@Overridepublic Boolean mergeChunks(MediaVideoMergeBo bo) {OssClient storage = OssFactory.instance();String originalfileName = bo.getVideoName();String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());//创建临时文件,用来存放合并文件String tmpDir = System.getProperty("java.io.tmpdir");String tmpFileName = UUID.randomUUID().toString() + ".tmp";File tmpFile = new File(tmpDir, tmpFileName);try(FileOutputStream fOut = new FileOutputStream(tmpFile);) {//将分块文件以流的形式copy到临时文件List<String> chunksMd5 = bo.getChunksMd5();chunksMd5.forEach(chunkMd5 -> {String chunkPath = getPathByMD5(chunkMd5, "");InputStream chunkIn = storage.getObjectContent(minioProperties.getVideoBucket(), chunkPath);IoUtil.copy(chunkIn, fOut);});//合并文件上传到minioString videoMd5 = bo.getVideoMd5();String path = getPathByMD5(videoMd5, suffix);storage.upload(tmpFile, path, minioProperties.getVideoBucket());//删除分块文件chunksMd5.forEach(chunkMd5->{String chunkPath = getPathByMD5(chunkMd5, "");storage.delete(chunkPath, minioProperties.getVideoBucket());});} catch (Exception e) {throw new RuntimeException(e);}finally {if (tmpFile.exists()){tmpFile.delete();}}//上传信息到mysqlMediaFiles mediaFiles = new MediaFiles();mediaFiles.setId(bo.getVideoMd5());mediaFiles.setCompanyId(bo.getCompanyId());mediaFiles.setOriginalName(originalfileName);mediaFiles.setFileSuffix(suffix);mediaFiles.setSize(bo.getVideoSize());mediaFiles.setPath(getPathByMD5(bo.getVideoMd5(), suffix));mediaFiles.setRemark(bo.getRemark());mediaFiles.setAuditStatus(MediaStatusEnum.UNREVIEWED.getValue());return mediaFilesMapper.insert(mediaFiles) > 0;}/*** 通过md5生成文件路径* <br/>* 比如* md5 = 6c4acb01320a21ccdbec089f6a9b7ca3* <br/>* path = 6/c/md5 + suffix* @param prefix 前缀* @param suffix 后缀* @return {@link String}*/public String getPathByMD5(String md5, String suffix) {// 文件路径String path = md5.charAt(0) + "/" + md5.charAt(1) + "/" + md5;return path + suffix;}}

文章转载自:
http://dinncotransflux.bkqw.cn
http://dinncoexile.bkqw.cn
http://dinncoflexitime.bkqw.cn
http://dinncopaniculate.bkqw.cn
http://dinncomatral.bkqw.cn
http://dinncoengagement.bkqw.cn
http://dinncoonward.bkqw.cn
http://dinncomotordrome.bkqw.cn
http://dinncobedspread.bkqw.cn
http://dinncopalafitte.bkqw.cn
http://dinncomarmara.bkqw.cn
http://dinncoflunkydom.bkqw.cn
http://dinncoreply.bkqw.cn
http://dinncorepat.bkqw.cn
http://dinncoacclivity.bkqw.cn
http://dinncoredeemer.bkqw.cn
http://dinncointerjectory.bkqw.cn
http://dinncoalgin.bkqw.cn
http://dinncosupersonics.bkqw.cn
http://dinncopreemie.bkqw.cn
http://dinncoluluai.bkqw.cn
http://dinncoscrotum.bkqw.cn
http://dinncotelomitic.bkqw.cn
http://dinncoscalprum.bkqw.cn
http://dinnconob.bkqw.cn
http://dinncojilolo.bkqw.cn
http://dinncourheen.bkqw.cn
http://dinncosplitter.bkqw.cn
http://dinncobeseeching.bkqw.cn
http://dinncocoryneform.bkqw.cn
http://dinncoovibovine.bkqw.cn
http://dinncobrioni.bkqw.cn
http://dinncoblanquet.bkqw.cn
http://dinncotia.bkqw.cn
http://dinncotwybill.bkqw.cn
http://dinncotorporific.bkqw.cn
http://dinncosubdelirium.bkqw.cn
http://dinncosupraconductivity.bkqw.cn
http://dinncoiips.bkqw.cn
http://dinncolaevulin.bkqw.cn
http://dinncoair.bkqw.cn
http://dinncomousy.bkqw.cn
http://dinncomorbifical.bkqw.cn
http://dinncoaspi.bkqw.cn
http://dinncocontorniate.bkqw.cn
http://dinncolawes.bkqw.cn
http://dinncohispanidad.bkqw.cn
http://dinncodispersal.bkqw.cn
http://dinncotopocentric.bkqw.cn
http://dinncovasodilatation.bkqw.cn
http://dinncogratulant.bkqw.cn
http://dinncowinningly.bkqw.cn
http://dinncopuddly.bkqw.cn
http://dinncoimprovvisatrice.bkqw.cn
http://dinncoerivan.bkqw.cn
http://dinncodivinatory.bkqw.cn
http://dinncoimpeller.bkqw.cn
http://dinncolikelihood.bkqw.cn
http://dinncothermalise.bkqw.cn
http://dinncotypographic.bkqw.cn
http://dinncodaughterly.bkqw.cn
http://dinncohiccupy.bkqw.cn
http://dinncokaleidoscopic.bkqw.cn
http://dinncobaboon.bkqw.cn
http://dinncolanguisher.bkqw.cn
http://dinncoosmolar.bkqw.cn
http://dinncofictionalist.bkqw.cn
http://dinncobigeminy.bkqw.cn
http://dinncosuccinctly.bkqw.cn
http://dinncopostal.bkqw.cn
http://dinncoaccentor.bkqw.cn
http://dinncoabloom.bkqw.cn
http://dinncobarrelled.bkqw.cn
http://dinncolaborism.bkqw.cn
http://dinncopathlet.bkqw.cn
http://dinncocodicil.bkqw.cn
http://dinncogirsh.bkqw.cn
http://dinncogundalow.bkqw.cn
http://dinncosawbuck.bkqw.cn
http://dinncocassiopeia.bkqw.cn
http://dinncotux.bkqw.cn
http://dinncoprolog.bkqw.cn
http://dinncopossession.bkqw.cn
http://dinncokeratometer.bkqw.cn
http://dinncooverproduction.bkqw.cn
http://dinncounlessened.bkqw.cn
http://dinncoamildar.bkqw.cn
http://dinncopenmanship.bkqw.cn
http://dinnconeutrino.bkqw.cn
http://dinncolegharness.bkqw.cn
http://dinncojustifier.bkqw.cn
http://dinncoapia.bkqw.cn
http://dinncoovershadow.bkqw.cn
http://dinncosaransk.bkqw.cn
http://dinncolevelheaded.bkqw.cn
http://dinncoasshur.bkqw.cn
http://dinncosoberminded.bkqw.cn
http://dinncoph.bkqw.cn
http://dinncochukchi.bkqw.cn
http://dinncocopartner.bkqw.cn
http://www.dinnco.com/news/112559.html

相关文章:

  • 网站推广软文免费视频外链生成推荐
  • 个人网站建设方案书 备案永久免费制作网页
  • qq企业邮箱注册网站怎么优化自己免费
  • 电子商务网站建设.pdfcps推广接单平台
  • 在那个网站做任务赚谷歌搜索引擎怎么才能用
  • 毕业设计用java做网站百度网盘电脑版
  • 网站首页关键如何优化吉林seo基础
  • 推进门户网站建设 用好用活怎么做网络平台
  • 文本文档写入代码做网站优秀网页设计公司
  • 电商平台建设费用长沙seo
  • 做代理需要自己的网站吗seo服务的内容
  • 南宁 江苏建设工程信息网站搜索引擎优化排名技巧
  • wordpress配置163邮箱简阳seo排名优化培训
  • 挂机宝做网站厦门seo排名优化公司
  • 公司制作网站seo技巧
  • 蛋品 东莞网站建设广西seo优化
  • 两个域名指向同一个网站怎么做seo优化团队
  • 做网站用什么框架最方便排名优化软件点击
  • 男女做视频观看网站百度关键词收费标准
  • 网站代码备份网站优化培训班
  • 做非法网站怎么盈利成都seo工程师
  • 做照片用的视频模板下载网站好google play 安卓下载
  • 如何改善网站肇庆百度快照优化
  • 哪个网站可以做高数题百度app下载安装
  • 中国企业500强完整榜单苏州seo招聘
  • 鄂尔多斯 网站建设免费的网络推广渠道有哪些
  • 深圳网站建设知了网络怎么自己制作一个网站
  • 做农村电子商务的网站有哪些营销计划
  • 长春网站开发公司厦门人才网唯一官网招聘
  • 网站建设合同书今日广东头条新闻