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

硅胶模具技术支持东莞网站建设seo算法培训

硅胶模具技术支持东莞网站建设,seo算法培训,网站建设里面包含什么语言,如何找网站建设客户文章目录 前言一、实现思路二、数据库表对象二、业务入参对象三、本地上传实现三、minio上传实现总结 前言 springboot 断点上传、续传、秒传实现。 保存方式提供本地上传(单机)和minio上传(可集群) 本文主要是后端实现方案&…

文章目录

  • 前言
  • 一、实现思路
  • 二、数据库表对象
  • 二、业务入参对象
  • 三、本地上传实现
  • 三、minio上传实现
  • 总结


前言

springboot 断点上传、续传、秒传实现。
保存方式提供本地上传(单机)和minio上传(可集群)
本文主要是后端实现方案,数据库持久化采用jpa

一、实现思路

  1. 前端生成文件md5,根据md5检查文件块上传进度或秒传

  2. 需要上传分片的文件上传分片文件

  3. 分片合并后上传服务器

二、数据库表对象

说明:

  1. AbstractDomainPd<String>为公共字段,如id,创建人,创建时间等,根据自己框架修改即可。
  2. clientId 应用id用于隔离不同应用附件,非必须
    附件表:上传成功的附件信息
@Entity
@Table(name = "gsdss_file", schema = "public")
@Data
public class AttachmentPO extends AbstractDomainPd<String> implements Serializable {/*** 相对路径*/private String path;/*** 文件名*/private String fileName;/*** 文件大小*/private String size;/*** 文件MD5*/private String fileIdentifier;
}

分片信息表:记录当前文件已上传的分片数据

@Entity
@Table(name = "gsdss_file_chunk", schema = "public")
@Data
public class ChunkPO extends AbstractDomainPd<String> implements Serializable {/*** 应用id*/private String clientId;/*** 文件块编号,从1开始*/private Integer chunkNumber;/*** 文件标识MD5*/private String fileIdentifier;/*** 文件名*/private String fileName;/*** 相对路径*/private String path;}

二、业务入参对象

检查文件块上传进度或秒传入参对象

package com.gsafety.bg.gsdss.file.manage.model.req;import io.swagger.v3.oas.annotations.Hidden;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.web.multipart.MultipartFile;import javax.validation.constraints.NotNull;@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ChunkReq {/*** 文件块编号,从1开始*/@NotNullprivate Integer chunkNumber;/*** 文件标识MD5*/@NotNullprivate String fileIdentifier;/*** 相对路径*/@NotNullprivate String path;/*** 块内容*/@Hiddenprivate MultipartFile file;/*** 应用id*/@NotNullprivate String clientId;/*** 文件名*/@NotNullprivate String fileName;
}

上传分片入参

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class CheckChunkReq {/*** 应用id*/@NotNullprivate String clientId;/*** 文件名*/@NotNullprivate String fileName;/*** md5*/@NotNullprivate String fileIdentifier;
}

分片合并入参

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class FileReq {@Hiddenprivate MultipartFile file;/*** 文件名*/@NotNullprivate String fileName;/*** 文件大小*/@NotNullprivate Long fileSize;/*** eg:data/plan/*/@NotNullprivate String path;/*** md5*/@NotNullprivate String fileIdentifier;/*** 应用id*/@NotNullprivate String clientId;
}

检查文件块上传进度或秒传返回结果

@Data
public class UploadResp implements Serializable {/*** 是否跳过上传(已上传的可以直接跳过,达到秒传的效果)*/private boolean skipUpload = false;/*** 已经上传的文件块编号,可以跳过,断点续传*/private List<Integer> uploadedChunks;/*** 文件信息*/private AttachmentResp fileInfo;}

三、本地上传实现

    @Resourceprivate S3OssProperties properties;@Resourceprivate AttachmentService attachmentService;@Resourceprivate ChunkDao chunkDao;@Resourceprivate ChunkMapping chunkMapping;/*** 上传分片文件** @param req*/@Overridepublic boolean uploadChunk(ChunkReq req) {BizPreconditions.checkArgumentNoStack(!req.getFile().isEmpty(), "上传分片不能为空!");BizPreconditions.checkArgumentNoStack(req.getPath().endsWith("/"), "url参数必须是/结尾");//文件名-1String fileName = req.getFileName().concat("-").concat(req.getChunkNumber().toString());//分片文件上传服务器的目录地址 文件夹地址/chunks/文件md5String filePath = properties.getPath().concat(req.getClientId()).concat(File.separator).concat(req.getPath()).concat("chunks").concat(File.separator).concat(req.getFileIdentifier()).concat(File.separator);try {Path newPath = Paths.get(filePath);Files.createDirectories(newPath);//文件夹地址/md5/文件名-1newPath = Paths.get(filePath.concat(fileName));if (Files.notExists(newPath)) {Files.createFile(newPath);}Files.write(newPath, req.getFile().getBytes(), StandardOpenOption.CREATE);} catch (IOException e) {log.error(" 附件存储失败 ", e);throw new BusinessCheckException("附件存储失败");}// 存储分片信息chunkDao.save(chunkMapping.req2PO(req));return true;}/*** 检查文件块*/@Overridepublic UploadResp checkChunk(CheckChunkReq req) {UploadResp result = new UploadResp();//查询数据库记录//先判断整个文件是否已经上传过了,如果是,则告诉前端跳过上传,实现秒传AttachmentResp resp = attachmentService.findByFileIdentifierAndClientId(req.getFileIdentifier(), req.getClientId());if (resp != null) {//当前文件信息另存AttachmentResp newResp = attachmentService.save(AttachmentReq.builder().fileName(req.getFileName()).origin(AttachmentConstants.TYPE.LOCAL_TYPE).clientId(req.getClientId()).path(resp.getPath()).size(resp.getSize()).fileIdentifier(req.getFileIdentifier()).build());result.setSkipUpload(true);result.setFileInfo(newResp);return result;}//如果完整文件不存在,则去数据库判断当前哪些文件块已经上传过了,把结果告诉前端,跳过这些文件块的上传,实现断点续传List<ChunkPO> chunkList = chunkDao.findByFileIdentifierAndClientId(req.getFileIdentifier(), req.getClientId());//将已存在的块的chunkNumber列表返回给前端,前端会规避掉这些块if (!CollectionUtils.isEmpty(chunkList)) {List<Integer> collect = chunkList.stream().map(ChunkPO::getChunkNumber).collect(Collectors.toList());result.setUploadedChunks(collect);}return result;}/*** 分片合并** @param req*/@Overridepublic boolean mergeChunk(FileReq req) {String filename = req.getFileName();String date = DateUtil.localDateToString(LocalDate.now());//附件服务器存储合并后的文件存放地址String file = properties.getPath().concat(req.getClientId()).concat(File.separator).concat(req.getPath()).concat(date).concat(File.separator).concat(filename);//服务器分片文件存放地址String folder = properties.getPath().concat(req.getClientId()).concat(File.separator).concat(req.getPath()).concat("chunks").concat(File.separator).concat(req.getFileIdentifier());//合并文件到本地目录,并删除分片文件boolean flag = mergeFile(file, folder, filename);if (!flag) {return false;}//保存文件记录AttachmentResp resp = attachmentService.findByFileIdentifierAndClientId(req.getFileIdentifier(), req.getClientId());if (resp == null) {attachmentService.save(AttachmentReq.builder().fileName(filename).origin(AttachmentConstants.TYPE.LOCAL_TYPE).clientId(req.getClientId()).path(file).size(FileUtils.changeFileFormat(req.getFileSize())).fileIdentifier(req.getFileIdentifier()).build());}//插入文件记录成功后,删除chunk表中的对应记录,释放空间chunkDao.deleteAllByFileIdentifierAndClientId(req.getFileIdentifier(), req.getClientId());return true;}/*** 文件合并** @param targetFile 要形成的文件地址* @param folder     分片文件存放地址* @param filename   文件的名称*/private boolean mergeFile(String targetFile, String folder, String filename) {try {//先判断文件是否存在if (FileUtils.fileExists(targetFile)) {//文件已存在return true;}Path newPath = Paths.get(StringUtils.substringBeforeLast(targetFile, File.separator));Files.createDirectories(newPath);Files.createFile(Paths.get(targetFile));Files.list(Paths.get(folder)).filter(path -> !path.getFileName().toString().equals(filename)).sorted((o1, o2) -> {String p1 = o1.getFileName().toString();String p2 = o2.getFileName().toString();int i1 = p1.lastIndexOf("-");int i2 = p2.lastIndexOf("-");return Integer.valueOf(p2.substring(i2)).compareTo(Integer.valueOf(p1.substring(i1)));}).forEach(path -> {try {//以追加的形式写入文件Files.write(Paths.get(targetFile), Files.readAllBytes(path), StandardOpenOption.APPEND);//合并后删除该块Files.delete(path);} catch (IOException e) {log.error(e.getMessage(), e);throw new BusinessException("文件合并失败");}});//删除空文件夹FileUtils.delDir(folder);} catch (IOException e) {log.error("文件合并失败: ", e);throw new BusinessException("文件合并失败");}return true;}

三、minio上传实现

    @Resourceprivate MinioTemplate minioTemplate;@Resourceprivate AttachmentService attachmentService;@Resourceprivate ChunkDao chunkDao;@Resourceprivate ChunkMapping chunkMapping;/*** 上传分片文件*/@Overridepublic boolean uploadChunk(ChunkReq req) {String fileName = req.getFileName();BizPreconditions.checkArgumentNoStack(!req.getFile().isEmpty(), "上传分片不能为空!");BizPreconditions.checkArgumentNoStack(req.getPath().endsWith(separator), "url参数必须是/结尾");String newFileName = req.getPath().concat("chunks").concat(separator).concat(req.getFileIdentifier()).concat(separator)+ fileName.concat("-").concat(req.getChunkNumber().toString());try {minioTemplate.putObject(req.getClientId(), newFileName, req.getFile());} catch (Exception e) {e.printStackTrace();throw new BusinessException("文件上传失败");}// 存储分片信息chunkDao.save(chunkMapping.req2PO(req));return true;}/*** 检查文件块*/@Overridepublic UploadResp checkChunk(CheckChunkReq req) {UploadResp result = new UploadResp();//查询数据库记录//先判断整个文件是否已经上传过了,如果是,则告诉前端跳过上传,实现秒传AttachmentResp resp = attachmentService.findByFileIdentifierAndClientId(req.getFileIdentifier(), req.getClientId());if (resp != null) {//当前文件信息另存AttachmentResp newResp = attachmentService.save(AttachmentReq.builder().fileName(req.getFileName()).origin(AttachmentConstants.TYPE.MINIO_TYPE).clientId(req.getClientId()).path(resp.getPath()).size(resp.getSize()).fileIdentifier(req.getFileIdentifier()).build());result.setSkipUpload(true);result.setFileInfo(newResp);return result;}//如果完整文件不存在,则去数据库判断当前哪些文件块已经上传过了,把结果告诉前端,跳过这些文件块的上传,实现断点续传List<ChunkPO> chunkList = chunkDao.findByFileIdentifierAndClientId(req.getFileIdentifier(), req.getClientId());//将已存在的块的chunkNumber列表返回给前端,前端会规避掉这些块if (!CollectionUtils.isEmpty(chunkList)) {List<Integer> collect = chunkList.stream().map(ChunkPO::getChunkNumber).collect(Collectors.toList());result.setUploadedChunks(collect);}return result;}/*** 分片合并** @param req*/@Overridepublic boolean mergeChunk(FileReq req) {String filename = req.getFileName();//合并文件到本地目录String chunkPath = req.getPath().concat("chunks").concat(separator).concat(req.getFileIdentifier()).concat(separator);List<Item> chunkList = minioTemplate.getAllObjectsByPrefix(req.getClientId(), chunkPath, false);String fileHz = filename.substring(filename.lastIndexOf("."));String newFileName = req.getPath() + UUIDUtil.uuid() + fileHz;try {List<ComposeSource> sourceObjectList = chunkList.stream().sorted(Comparator.comparing(Item::size).reversed()).map(l -> ComposeSource.builder().bucket(req.getClientId()).object(l.objectName()).build()).collect(Collectors.toList());ObjectWriteResponse response = minioTemplate.composeObject(req.getClientId(), newFileName, sourceObjectList);//删除分片bucket及文件minioTemplate.removeObjects(req.getClientId(), chunkPath);} catch (Exception e) {e.printStackTrace();throw new BusinessException("文件合并失败");}//保存文件记录AttachmentResp resp = attachmentService.findByFileIdentifierAndClientId(req.getFileIdentifier(), req.getClientId());if (resp == null) {attachmentService.save(AttachmentReq.builder().fileName(filename).origin(AttachmentConstants.TYPE.MINIO_TYPE).clientId(req.getClientId()).path(newFileName).size(FileUtils.changeFileFormat(req.getFileSize())).fileIdentifier(req.getFileIdentifier()).build());}//插入文件记录成功后,删除chunk表中的对应记录,释放空间chunkDao.deleteAllByFileIdentifierAndClientId(req.getFileIdentifier(), req.getClientId());return true;}

MinioTemplate 参考

总结

  1. 检查文件块上传进度或秒传

根据文件md5查询附件信息表,如果存在,直接返回附件信息。
不存在查询分片信息表,查询当前文件分片上传进度,返回已经上传过的分片编号

  1. 上传分片

分片文件上传地址需要保证唯一性,可用文件MD5作为隔离
上传后保存分片上传信息
minio对合并分片文件有大小限制,除最后一个分片外,其他分片文件大小不得小于5MB,所以minio分片上传需要分片大小最小为5MB,并且获取分片需要按照分片文件大小排序,将最后一个分片放到最后进行合并

  1. 分片合并

将分片文件合并为新文件到最终文件存放地址并删除分片文件
保存最终文件信息到附件信息表
删除对应分片信息表数据

在这里插入图片描述


文章转载自:
http://superinfection.qkrz.cn
http://succumb.qkrz.cn
http://surrenderee.qkrz.cn
http://teledrama.qkrz.cn
http://thenardite.qkrz.cn
http://safranine.qkrz.cn
http://epicureanism.qkrz.cn
http://nitwitted.qkrz.cn
http://tranter.qkrz.cn
http://blindness.qkrz.cn
http://consigner.qkrz.cn
http://dizzyingly.qkrz.cn
http://hobbadehoy.qkrz.cn
http://tenthly.qkrz.cn
http://technofear.qkrz.cn
http://tourer.qkrz.cn
http://lararium.qkrz.cn
http://diuretic.qkrz.cn
http://gothicize.qkrz.cn
http://unambivalent.qkrz.cn
http://touch.qkrz.cn
http://nones.qkrz.cn
http://raptor.qkrz.cn
http://luminaire.qkrz.cn
http://incendijel.qkrz.cn
http://cote.qkrz.cn
http://ebro.qkrz.cn
http://output.qkrz.cn
http://frivolity.qkrz.cn
http://langouste.qkrz.cn
http://addiction.qkrz.cn
http://giftware.qkrz.cn
http://tiran.qkrz.cn
http://accoutrement.qkrz.cn
http://faker.qkrz.cn
http://falciform.qkrz.cn
http://overdue.qkrz.cn
http://rip.qkrz.cn
http://merrymaking.qkrz.cn
http://photolithoprint.qkrz.cn
http://aerolitics.qkrz.cn
http://liverleaf.qkrz.cn
http://otherworldly.qkrz.cn
http://unthatch.qkrz.cn
http://proscribe.qkrz.cn
http://haytian.qkrz.cn
http://grue.qkrz.cn
http://eject.qkrz.cn
http://shorthanded.qkrz.cn
http://unwatered.qkrz.cn
http://anglophone.qkrz.cn
http://tippler.qkrz.cn
http://dib.qkrz.cn
http://kincardinshire.qkrz.cn
http://duster.qkrz.cn
http://churchlike.qkrz.cn
http://sheraton.qkrz.cn
http://exhaustive.qkrz.cn
http://hesiodian.qkrz.cn
http://inche.qkrz.cn
http://norwalk.qkrz.cn
http://dopester.qkrz.cn
http://intoxication.qkrz.cn
http://patronite.qkrz.cn
http://mantissa.qkrz.cn
http://curly.qkrz.cn
http://irrelevant.qkrz.cn
http://firebrat.qkrz.cn
http://sanforized.qkrz.cn
http://barrett.qkrz.cn
http://ethidium.qkrz.cn
http://cristate.qkrz.cn
http://hagseed.qkrz.cn
http://boson.qkrz.cn
http://equicaloric.qkrz.cn
http://mucosity.qkrz.cn
http://billsticking.qkrz.cn
http://monophagia.qkrz.cn
http://etymologic.qkrz.cn
http://caldron.qkrz.cn
http://lorikeet.qkrz.cn
http://typographer.qkrz.cn
http://hemicellulose.qkrz.cn
http://jalap.qkrz.cn
http://shindy.qkrz.cn
http://mohel.qkrz.cn
http://viscount.qkrz.cn
http://simul.qkrz.cn
http://filiation.qkrz.cn
http://grungy.qkrz.cn
http://etaerio.qkrz.cn
http://recognizable.qkrz.cn
http://coursed.qkrz.cn
http://turret.qkrz.cn
http://turd.qkrz.cn
http://chiefless.qkrz.cn
http://siesta.qkrz.cn
http://unequivocal.qkrz.cn
http://mneme.qkrz.cn
http://precipitance.qkrz.cn
http://www.hrbkazy.com/news/87522.html

相关文章:

  • 网站程序预装软文范例100字
  • 灵犀科技 高端网站建设背景图友情链接是免费的吗
  • 工程公司logo图标设计优化大师怎么样
  • 杭州蚂蚁 做网站的公司做网络推广有前途吗
  • 网站建设品牌公司搜索百度网址网页
  • html做校园网站营销型网站建设专家
  • 百度站长平台清退搜索引擎营销案例分析题
  • 中国室内设计网站排名网络营销方法有哪些?
  • 广州中企动力网站制作百度客服人工电话24
  • 沈阳哪家做网站好seo优化网站排名
  • 甘肃庆阳西峰区疫情seo营销外包公司
  • 洋桥网站建设公司手机导航下载2022新版
  • 亚马逊网站托管怎么做网站上做推广
  • flash网站建设技术搜索词热度查询
  • 个人做百度云下载网站吗网站排名推广推荐
  • 做视频图片博客网站有哪些企业网络营销推广方案策划范文
  • 云虚拟主机可以做多少个网站网络推广的公司是骗局吗
  • 网站下方一般放什么对网络营销的认识有哪些
  • wordpress如何进入后台上海排名seo公司
  • 中国优秀网站广州网站优化排名
  • pc网站和手机网站想做电商应该怎么入门
  • 做网站要用到的技术线上电商怎么做
  • 网站建设对服务器有舍要求吗爱网站查询
  • 怎样跟网站做优化呢国内高清视频素材网站推荐
  • 东平专业的网站制作优化设计答案大全英语
  • 现代网站建设百度购物平台客服电话
  • 北京公司网站制作方法北京seo关键词
  • 网站百度指数分析宁波网络营销策划公司
  • 常熟网站制作全网营销推广是什么
  • 超可爱做头像的网站百度今日小说排行榜