如何用网站做cpa,免费下载的app,网站备案更改吗,山东省建设执业资格注册管理中心网站背景#xff1a; 公司后台管理系统有个需求#xff0c;需要上传体积比较大的文件#xff1a;500M#xff0d;1024M#xff1b;此时普通的文件上传显然有些吃力了#xff0c;加上我司服务器配置本就不高#xff0c;带宽也不大#xff0c;所以必须考虑多线程异步上传来提…背景 公司后台管理系统有个需求需要上传体积比较大的文件500M1024M此时普通的文件上传显然有些吃力了加上我司服务器配置本就不高带宽也不大所以必须考虑多线程异步上传来提速所以这里就要用到文件分片上传技术了。 技术选型 直接问GPT实现大文件分片上传比较好的解决方案它给的答案是webUploader链接是官方文档这是由 Baidu FEX 团队开发的一款以 HTML5 为主FLASH 为辅的现代文件上传组件。在现代的浏览器里面能充分发挥 HTML5 的优势同时又不摒弃主流IE浏览器沿用原来的 FLASH 运行时兼容 IE6iOS 6, android 4。采用大文件分片并发上传极大的提高了文件上传效率功能强大且齐全支持对文件内容的Hash计算和分片上传可实现上传进度条等功能。 实现原理 文件分片上传比较简单就不画图了前端(webUploader)将用户选择的文件根据开发者配置的分片参数进行分片计算将文件分成N个小文件多次调用后端提供的分片文件上传接口webUploader插件有默认的一套参数规范文件ID及分片相关字段后端将对保存分片临时文件后端记录并判断当前文件所有分片是否上传完毕若已上传完则将所有分片合并成完整的文件完成后建议删除分片临时文件若考虑做分片下载可以保留。 前端引入webUploader
这里推荐去CDN下载静态资源 记得要先引入JQuerywebUploader依赖JQuery前端页面引入CSS和JS文件即可Uploader.swf文件在创建webUploader对象时指定貌似用来做兼容的。
前端笔者前端用的layui核心代码
//百度文件上传插件 WebUploaderlet uploader WebUploader.create({// 选完文件后是否自动上传。auto: true,// swf文件路径swf: contextPath /static/plugin/webuploader/Uploader.swf,pick: {id: #webUploader,multiple: false},// 文件接收服务端。server: contextPath /common/file/shard/upload,// 文件分片上传相关配置chunked: true,chunkSize: 5 * 1024 * 1024, // 分片大小为 5MBchunkRetry: 3, // 上传失败最大重试次数threads: 5, // 同时最大上传线程数});//文件上传临时对象let fileUpload {idPrefix: //文件id前缀, genIdPrefix: function () {this.idPrefix new Date().getTime() _;}, mergeLoading: null //合并文件加载层, lastUploadResponse: null // 最后一次上传返回值, chunks: 0 // 文件分片数, uploadedChunks: 0 // 已上传文件分片数, sumUploadChunk: function () {if (this.chunks 0) {this.uploadedChunks;}}, checkResult: function () {if (this.uploadedChunks this.chunks) {layer.open({title: 系统提示, content: 文件上传失败请重新上传, btn: [我知道了]});}}};// 某个文件开始上传前触发一个文件只会触发一次uploader.on(uploadStart, function (file) {$(#uploadProgressBar).show();// 生成文件id前缀fileUpload.genIdPrefix();});// 当某个文件的分块在发送前触发主要用来询问是否要添加附带参数大文件在开起分片上传的前提下此事件可能会触发多次uploader.on(uploadBeforeSend, function (object, data, header) {// 重写文件id生成规则data.id fileUpload.idPrefix data.name;fileUpload.chunks data.chunks ! null ? data.chunks : 0;});uploader.on(uploadProgress, function (file, percentage) {// 更新进度条let value Math.round(percentage * 100);element.progress(progressBar, value %);if (value 100) {fileUpload.mergeLoading layer.load();}});// 获取最后上传成功的文件信息每个分片文件上传都会回调uploader.on(uploadAccept, function (file, response) {if (response null || response.code ! 0000) {return;}fileUpload.sumUploadChunk();if (response.data ! null response.data.fileAccessPath ! null) {fileUpload.lastUploadResponse response.data;}});// 文件上传成功时触发uploader.on(uploadSuccess, function (file, response) {console.log(File file.name uploaded successfully.);layer.msg(文件上传成功);$(#fileName).val(fileUpload.lastUploadResponse.fileOriginalName);$(#fileRelativePath).val(fileUpload.lastUploadResponse.fileRelativePath);})uploader.on(uploadComplete, function (file) {console.log(File file.name uploaded complete.);console.log(总分片 fileUpload.chunks 已上传 fileUpload.uploadedChunks);fileUpload.checkResult();$(#uploadProgressBar).hide();layer.close(fileUpload.mergeLoading);});
其中几个关键的节点的事件回调都提供了使用起来很方便其中“uploadProgress”事件实现了上传的实时进度条展示。 后端Controller代码 /*** 文件分片上传* * * param file* param fileUploadInfoDTO* return*/PostMapping(value shard/upload)public LayuiFileUploadService.FileBean uploadFileByShard(RequestParam(file) MultipartFile file,FileUploadInfoDTO fileUploadInfoDTO) {if (null fileUploadInfoDTO) {return Layui.error(文件信息为空);}if (null file || file.getSize() 0) {return Layui.error(文件内容为空);}log.info(fileName[{}], file.getName());log.info(fileSize[{}], file.getSize());log.info(fileShardUpload[{}], JSONUtil.toJsonStr(fileUploadInfoDTO));FileUploadService.FileBean fileBean fileShardUploadService.uploadFileByShard(fileUploadInfoDTO, file);return Layui.success(fileBean);}/*** Author: XiangPeng* Date: 2023/12/22 12:01*/Getter
Setter
public class FileUploadInfoDTO implements Serializable {private static final long serialVersionUID -1L;/*** 文件 ID*/private String id;/*** 文件名*/private String name;/*** 文件类型*/private String type;/*** 文件最后修改日期*/private String lastModifiedDate;/*** 文件大小*/private Long size;/*** 分片总数*/private int chunks;/*** 当前分片序号*/private int chunk;
}Getter
Setter
public class FileUploadCacheDTO implements Serializable {private static final long serialVersionUID 1L;/*** 文件 ID*/private String id;/*** 文件名*/private String name;/*** 分片总数*/private int chunks;/*** 当前已上传分片索引*/private ListInteger uploadedChunkIndex;public FileUploadCacheDTO(FileUploadInfoDTO fileUploadInfoDTO) {this.id fileUploadInfoDTO.getId();this.name fileUploadInfoDTO.getName();this.chunks fileUploadInfoDTO.getChunks();this.uploadedChunkIndex Lists.newArrayList();}public FileUploadCacheDTO() {}
}
后端Service层代码 /*** 文件分片上传* * param fileUploadInfoDTO* param file* return*/public FileBean uploadFileByShard(FileUploadInfoDTO fileUploadInfoDTO, MultipartFile file) {if (fileUploadInfoDTO null || file null) {throw new ServiceException(文件上传失败);}// 无需分片的小文件直接上传if (fileUploadInfoDTO.getChunks() 0) {return super.commonUpload(file);}String fileId fileUploadInfoDTO.getId();// 生成分片临时文件文件名格式文件id_分片序号FileBean fileBean super.commonUpload(fileId StrUtil.UNDERLINE fileUploadInfoDTO.getChunk(), file);// redis缓存数据FileUploadCacheDTO fileUploadInfo null;synchronized (this) {// 查询文件id是否存在不存在则创建存在则更新已上传分片数fileUploadInfo (FileUploadCacheDTO) redisService.get(genRedisKey(fileId));// 第一个分片文件上传if (fileUploadInfo null) {fileUploadInfo new FileUploadCacheDTO(fileUploadInfoDTO);}fileUploadInfo.getUploadedChunkIndex().add(fileUploadInfoDTO.getChunk());redisService.set(genRedisKey(fileId), fileUploadInfo);// 判断所有分片文件是否上传完成if ((fileUploadInfo.getUploadedChunkIndex().size()) fileUploadInfo.getChunks()) {return fileBean;}}// 合并文件return mergeChunks(fileUploadInfo);}/*** 分片文件全部上传完成则合并文件清除缓存并返回文件地址* * param fileUploadCache* return*/private FileBean mergeChunks(FileUploadCacheDTO fileUploadCache) {String mergeFileRelativePath super.getCommonPath().getFileRelativePath() fileUploadCache.getId();String mergeFilePath super.getCommonPath().getBasePath() mergeFileRelativePath;RandomAccessFile mergedFile null;File chunkTempFile null;RandomAccessFile chunkFile null;try {mergedFile new RandomAccessFile(mergeFilePath, rw);for (int i 0; i fileUploadCache.getChunks(); i) {// 读取分片文件chunkTempFile new File(super.getCommonPath().getFileFullPath() fileUploadCache.getId() StrUtil.UNDERLINE i);byte[] buffer new byte[1024 * 1024];int bytesRead;chunkFile new RandomAccessFile(chunkTempFile, r);// 合并分片文件while ((bytesRead chunkFile.read(buffer)) ! -1) {mergedFile.write(buffer, 0, bytesRead);}chunkFile.close();}} catch (IOException e) {log.error(merge file chunk error, fileId[{}], fileUploadCache.getId(), e);} finally {try {if (mergedFile ! null) {mergedFile.close();}} catch (IOException e) {}redisService.remove(genRedisKey(fileUploadCache.getId()));// 删除分片文件removeChunkFiles(super.getCommonPath().getFileFullPath(), fileUploadCache);}return FileBean.builder().fileOriginalName(fileUploadCache.getName()).fileRelativePath(mergeFileRelativePath).fileAccessPath(super.getNginxPath() mergeFileRelativePath).build();}private void removeChunkFiles(String fileFullPathPrefix, FileUploadCacheDTO fileUploadCache) {taskExecutor.execute(() - {try {// 延迟1秒删除TimeUnit.SECONDS.sleep(1);String fileFullPath;for (int i 0; i fileUploadCache.getChunks(); i) {try {fileFullPath fileFullPathPrefix fileUploadCache.getId() StrUtil.UNDERLINE i;FileUtil.del(fileFullPath);log.info(file[{}] delete success., fileFullPath);} catch (Exception e) {log.error(delete temp file error., e);}}} catch (Exception e) {log.error(delete temp chunk file error., e);}});}private String genRedisKey(String id) {return FILE_SHARD_UPLOAD_KEY id;}