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

网站设计 布局南京 网站制作公司

网站设计 布局,南京 网站制作公司,公司网站内容相近,淘宝搜索关键词查询工具项目场景#xff1a; 将大文件分割成小的片段#xff0c;然后通过多个请求并行上传这些片段#xff0c;最终在服务器端将这些片段合并还原为完整的文件。这种方式有助于规避一些上传过程中的问题#xff0c;如网络不稳定、上传中断等#xff0c;并能提高上传速度。 解决方…项目场景 将大文件分割成小的片段然后通过多个请求并行上传这些片段最终在服务器端将这些片段合并还原为完整的文件。这种方式有助于规避一些上传过程中的问题如网络不稳定、上传中断等并能提高上传速度。 解决方案 后端技术Spring BootMyBatisMySql 前端分为两种都是使用spark-md5.js         1、vue使用插件vue-simple-uploader         2、jquery前端项目只有一个页面使用原生的方式方便学习 后端服务只有一个接口是通用的文章里前端我以jquery的为示例方便大家参考。 源码里面两种方式都包括了https://download.csdn.net/download/u011974797/86862270 1、文件上传         小文件图片、文档、视频上传可以直接使用很多ui框架封装的上传组件或者自己写一个input 上传利用FormData 对象提交文件数据后端使用spring提供的MultipartFile进行文件的接收然后写入即可。但是对于比较大的文件比如上传2G左右的文件http上传就需要将文件分片上传file.slice()否则中间http长时间连接可能会断掉。 2、分片上传         分片上传就是将所要上传的文件按照一定的大小将整个文件分隔成多个数据块我们称之为Part来进行分别上传上传完之后再由服务端对所有上传的文件进行汇总整合成原始的文件。 3、秒传         通俗的说你把要上传的东西上传通过spark-md5.js转md5服务器会先做MD5校验如果服务器上有一样的东西它就直接给你个新地址其实你下载的都是服务器上的同一个文件想要不秒传其实只要让MD5改变就是对文件本身做一下修改改名字不行例如一个文本文件你多加几个字MD5就变了就不会秒传了. 4、断点续传         断点续传是在下载或上传时将下载或上传任务一个文件或一个压缩包人为的划分为几个部分每一个部分采用一个线程进行上传或下载如果碰到网络故障可以从已经上传或下载的部分开始继续上传或者下载未完成的部分而没有必要从头开始上传或者下载。本文的断点续传主要是针对断点上传场景。 源码 关键代码就是Controller和Serviceentity和返回结果Result可以按自己的框架来 Controller package com.test.controller;import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.OutputStream; import java.net.URLEncoder;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController;import com.baomidou.mybatisplus.core.metadata.IPage; import com.test.entity.ChunkEntity; import com.test.entity.FileListEntity; import com.test.query.FileQuery; import com.test.service.FileService; import com.test.utils.Result; import com.test.vo.UploadResult;import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j;RestController RequestMapping(/file) Api(tags { 测试文件上传 }) Slf4j public class TestFileController {Value(${testfile.filePath})private String filePath;Autowiredprivate FileService fileService;/*** 上传文件块* vue-simple-uploader会调用post方法上传*/ApiOperation(value 上传文件块)RequestMapping(value /upload, method RequestMethod.POST)public Result? uploadChunk(ChunkEntity chunkEntity) {log.info(文件名: {}, chunkNumber: {}, chunkEntity.getFilename(), chunkEntity.getChunkNumber());boolean flag fileService.uploadChunk(chunkEntity);if(flag){return Result.ok();}return Result.error();}/*** 检查文件块* vue-simple-uploader会调用get方法验证*/ApiOperation(value 检查文件块)RequestMapping(value /upload, method RequestMethod.GET)public Result? checkChunk(ChunkEntity chunkEntity) {UploadResult result fileService.checkChunk(chunkEntity);return Result.ok(result);}/*** 合并文件*/ApiOperation(value 合并文件)RequestMapping(value /merge, method RequestMethod.POST)public Result? merge(RequestBody FileListEntity fileInfo) {boolean flag fileService.merge(fileInfo);if(flag){return Result.ok();}return Result.error();}/*** 查询列表*/ApiOperation(value 查询列表)RequestMapping(value /selectFileList, method RequestMethod.POST)public Result? selectFileList(RequestBody FileQuery fileQuery) {IPageFileListEntity ipage fileService.selectFileList(fileQuery);return Result.ok(ipage);}}Service package com.test.service;import java.io.IOException; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.List; import java.util.stream.Collectors;import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.test.entity.ChunkEntity; import com.test.entity.FileListEntity; import com.test.mapper.ChunkDao; import com.test.mapper.FileListDao; import com.test.query.FileQuery; import com.test.vo.UploadResult;import lombok.extern.slf4j.Slf4j;Service Slf4j public class FileService extends ServiceImplChunkDao, ChunkEntity {Value(${testfile.filePath})private String filePath;Autowiredprivate FileListDao fileListDao;/*** 上传文件块* param chunkEntity 文件块* return true成功*/public boolean uploadChunk(ChunkEntity chunkEntity) {Path path Paths.get(generatePath(filePath, chunkEntity));try {Files.write(path, chunkEntity.getUpfile().getBytes());log.debug(文件 {} 写入成功, md5:{}, chunkEntity.getFilename(), chunkEntity.getIdentifier());//写入数据库this.save(chunkEntity);} catch (IOException e) {log.error(上传文件块失败: e);return false;}return true;}/*** 检查文件块* param chunkEntity 文件块* return*/public UploadResult checkChunk(ChunkEntity chunkEntity) {UploadResult result new UploadResult();//查询本地磁盘和数据库记录选一种方式/*//直接查询本地磁盘String file filePath / chunkEntity.getIdentifier() / chunkEntity.getFilename();//先判断整个文件是否已经上传过了如果是则告诉前端跳过上传实现秒传if(fileExists(file)) {result.setSkipUpload(true);return result;}*///查询数据库记录//先判断整个文件是否已经上传过了如果是则告诉前端跳过上传实现秒传QueryWrapperFileListEntity fileWrapper new QueryWrapperFileListEntity();fileWrapper.lambda().eq(FileListEntity::getDelFlag, 0);fileWrapper.lambda().eq(FileListEntity::getIdentifier, chunkEntity.getIdentifier());FileListEntity fileListEntity fileListDao.selectOne(fileWrapper);if (fileListEntity ! null) {result.setSkipUpload(true);result.setFileId(fileListEntity.getId());return result;}//如果完整文件不存在则去数据库判断当前哪些文件块已经上传过了把结果告诉前端跳过这些文件块的上传实现断点续传QueryWrapperChunkEntity chunkWrapper new QueryWrapperChunkEntity();chunkWrapper.lambda().eq(ChunkEntity::getIdentifier, chunkEntity.getIdentifier());ListChunkEntity chunkList this.list(chunkWrapper);//将已存在的块的chunkNumber列表返回给前端,前端会规避掉这些块if (!CollectionUtils.isEmpty(chunkList)) {ListInteger collect chunkList.stream().map(ChunkEntity::getChunkNumber).collect(Collectors.toList());result.setUploadedChunks(collect);}return result;}Transactional(rollbackFor Exception.class)public boolean merge(FileListEntity fileInfo) {String filename fileInfo.getFilename();String file filePath / fileInfo.getIdentifier() / filename;String folder filePath / fileInfo.getIdentifier();boolean flag mergeFile(file, folder, filename);if(!flag){return false;}//当前文件已存在数据库中时,返回已存在标识QueryWrapperFileListEntity fileWrapper new QueryWrapperFileListEntity();fileWrapper.lambda().eq(FileListEntity::getDelFlag, 0);fileWrapper.lambda().eq(FileListEntity::getIdentifier, fileInfo.getIdentifier());Integer count fileListDao.selectCount(fileWrapper);if (count 0) {fileInfo.setLocation(file);fileListDao.insert(fileInfo);}//插入文件记录成功后,删除chunk表中的对应记录,释放空间QueryWrapperChunkEntity chunkWrapper new QueryWrapperChunkEntity();chunkWrapper.lambda().eq(ChunkEntity::getIdentifier, fileInfo.getIdentifier());this.remove(chunkWrapper);return true;}/*** 查看应用列表*/public IPageFileListEntity selectFileList(FileQuery fileQuery) {QueryWrapperFileListEntity qw new QueryWrapper();qw.lambda().eq(FileListEntity::getDelFlag, 0);qw.lambda().like(StringUtils.isNotBlank(fileQuery.getName()), FileListEntity::getFilename, fileQuery.getName());qw.lambda().orderByDesc(FileListEntity::getCreateTime);IPageFileListEntity iPage fileListDao.selectPage(new Page(fileQuery.getPage(), fileQuery.getLimit()), qw);return iPage;}/*** 功能描述:生成块文件所在地址*/private String generatePath(String uploadFolder, ChunkEntity chunk) {StringBuilder sb new StringBuilder();//文件夹地址/md5sb.append(uploadFolder).append(/).append(chunk.getIdentifier());//判断uploadFolder/identifier 路径是否存在不存在则创建if (!Files.isWritable(Paths.get(sb.toString()))) {log.info(path not exist,create path: {}, sb.toString());try {Files.createDirectories(Paths.get(sb.toString()));} catch (IOException e) {log.error(e.getMessage(), e);}}//文件夹地址/md5/文件名-1return sb.append(/).append(chunk.getFilename()).append(-).append(chunk.getChunkNumber()).toString();}/*** 文件合并** param targetFile 要形成的文件名* param folder 要形成的文件夹地址* param filename 文件的名称*/private boolean mergeFile(String targetFile, String folder, String filename) {try {//先判断文件是否存在if(fileExists(targetFile)) {//文件已存在return true;}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);}});} catch (IOException e) {log.error(文件合并失败: , e);return false;}return true;}/*** 根据文件的全路径名判断文件是否存在* param file* return*/private boolean fileExists(String file) {boolean fileExists false;Path path Paths.get(file);fileExists Files.exists(path,new LinkOption[]{ LinkOption.NOFOLLOW_LINKS});return fileExists;}}entity package com.test.entity;import org.springframework.web.multipart.MultipartFile;import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName;import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data;TableName(t_chunk) Data ApiModel(value 文件块, description 文件块) public class ChunkEntity {/*** 主键ID*/TableIdApiModelProperty(value 主键ID)private String id;/*** 文件块编号从1开始*/ApiModelProperty(value 文件块编号从1开始)private Integer chunkNumber;/*** 每块大小*/ApiModelProperty(value 每块大小)private Long chunkSize;/*** 当前分块大小*/ApiModelProperty(value 当前分块大小)private Long currentChunkSize;/*** 总大小*/ApiModelProperty(value 总大小)private Long totalSize;/*** 文件标识MD5*/ApiModelProperty(value 文件标识MD5)private String identifier;/*** 文件名*/ApiModelProperty(value 文件名)private String filename;/*** 相对路径*/ApiModelProperty(value 相对路径)private String relativePath;/*** 总块数*/ApiModelProperty(value 总块数)private Integer totalChunks;/*** 块内容*/TableField(exist false)ApiModelProperty(value 块内容)private MultipartFile upfile;}package com.test.entity;import java.util.Date;import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat;import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data;TableName(t_file_list) Data ApiModel(value 文件对象, description 文件对象) public class FileListEntity {/*** 主键ID*/TableIdApiModelProperty(value 主键ID)private String id;/*** 文件名*/ApiModelProperty(value 文件名)private String filename;/*** 文件标识MD5*/ApiModelProperty(value 文件标识MD5)private String identifier;/*** 总大小*/ApiModelProperty(value 总大小)private Long totalSize;/*** 地址*/ApiModelProperty(value 地址)private String location;/*** 是否删除: 0.否 1.是*/ApiModelProperty(value 是否删除: 0.否 1.是)private Integer delFlag;/*** 创建时间*/ApiModelProperty(value 创建时间, hidden true)JsonFormat(pattern yyyy-MM-dd HH:mm:ss, timezone GMT8)private Date createTime;/*** 文件大小带单位*/TableField(exist false)ApiModelProperty(value 文件大小带单位)private String totalSizeName;public void setTotalSize(Long totalSize) {this.totalSize totalSize;if(1024*1024 this.totalSize this.totalSize 1024 ) {this.totalSizeName String.format(%.2f,this.totalSize.doubleValue()/1024) KB;}else if(1024*1024*1024 this.totalSize this.totalSize 1024*1024 ) {this.totalSizeName String.format(%.2f,this.totalSize.doubleValue()/(1024*1024)) MB;}else if(this.totalSize 1024*1024*1024 ) {this.totalSizeName String.format(%.2f,this.totalSize.doubleValue()/(1024*1024*1024)) GB;}else {this.totalSizeName this.totalSize.toString() B;}}}返回接口封装Result package com.test.utils;import java.io.Serializable;import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data;/*** 接口返回数据格式**/ Data ApiModel(value 接口返回对象, description 接口返回对象) public class ResultT implements Serializable {private static final long serialVersionUID 1L;/*** 成功标志*/ApiModelProperty(value 成功标志)private boolean success true;/*** 返回处理消息*/ApiModelProperty(value 返回处理消息)private String msg CodeMsg.SUCCESS.getMsg();/*** 返回代码*/ApiModelProperty(value 返回代码)private Integer code CodeMsg.SUCCESS.getCode();/*** 返回数据对象 data*/ApiModelProperty(value 返回数据对象)private Object data;/*** 时间戳*/ApiModelProperty(value 时间戳)private long timestamp System.currentTimeMillis();public Result() {}public ResultT success(String msg) {this.msg msg;this.code CodeMsg.SUCCESS.getCode();this.success true;return this;}public static ResultObject ok() {ResultObject r new ResultObject();r.setSuccess(true);r.setCode(CodeMsg.SUCCESS.getCode());r.setMsg(CodeMsg.SUCCESS.getMsg());return r;}public static ResultObject ok(String msg) {ResultObject r new ResultObject();r.setSuccess(true);r.setCode(CodeMsg.SUCCESS.getCode());r.setMsg(msg);return r;}public static ResultObject ok(Object data) {ResultObject r new ResultObject();r.setSuccess(true);r.setCode(CodeMsg.SUCCESS.getCode());r.setData(data);return r;}public static ResultObject error() {ResultObject r new ResultObject();r.setCode(CodeMsg.SYSTEM_ERROR.getCode());r.setMsg(CodeMsg.SYSTEM_ERROR.getMsg());r.setSuccess(false);return r;}public static ResultObject error(String msg) {return error(CodeMsg.SYSTEM_ERROR.getCode(), msg);}public static ResultObject error(int code, String msg) {ResultObject r new ResultObject();r.setCode(code);r.setMsg(msg);r.setSuccess(false);return r;}} 表结构 DROP TABLE IF EXISTS t_chunk; CREATE TABLE t_chunk (id varchar(64) NOT NULL COMMENT 主键ID,chunk_number int(11) NOT NULL COMMENT 文件块编号从1开始,chunk_size bigint(20) NOT NULL COMMENT 分块大小,current_chunk_size bigint(20) NOT NULL COMMENT 当前分块大小,identifier varchar(64) NOT NULL COMMENT 文件标识MD5,filename varchar(500) NOT NULL COMMENT 文件名,relative_path varchar(500) NOT NULL COMMENT 相对路径,total_chunks int(11) NOT NULL COMMENT 总块数,total_size bigint(20) NOT NULL COMMENT 总大小,PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8 COMMENT文件块; DROP TABLE IF EXISTS t_file_list; CREATE TABLE t_file_list (id varchar(64) NOT NULL COMMENT 主键ID,filename varchar(500) NOT NULL COMMENT 文件名,identifier varchar(64) NOT NULL COMMENT 唯一标识MD5,total_size bigint(20) NOT NULL COMMENT 文件总大小,location varchar(200) NOT NULL COMMENT 地址,del_flag int(1) NOT NULL DEFAULT 0 COMMENT 是否删除: 0.否 1.是,create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间,PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8 COMMENT文件列表; 前端代码 !DOCTYPE HTML html headmeta charsetutf-8title大文件分片上传示例/titlescript srcjquery.min.js/scriptscript srcspark-md5.js/scriptscript//vari -1;var chunkSize 5 * 1024 * 1024; //以5MB为一个分片var succeed 0;var currentIndex 0;var shardCount 0;var databgein; //开始时间var dataend; //结束时间var page {init: function () {$(#upload).click(function () {//清空$(#usetime).text();$(#param).text();$(#output).text();databgein new Date();var file $(#file)[0].files[0]; //文件对象if(file null){alert(文件不能为空);return;}computeMD5(file);});$(#querybut).click(function () {var json {page:1,limit: 10,name: };//Ajax提交$.ajax({url: http://localhost:8080/fileservice/file/selectFileList,type: POST,data: JSON.stringify(json),dataType: json,async: true, //异步contentType: application/json;charsetutf-8,success: function (data) {$(#filediv).text();if(data.data.records ! null){$(data.data.records).each(function(){var tr $(#filediv).append(tr/tr);tr.append(td this.identifier /td);tr.append(td this.filename /td);tr.append(td this.location /td);})}}, error: function (XMLHttpRequest, textStatus, errorThrown) {alert(服务器出错!);}});});}};$(function () {page.init();});/*** 计算md5实现断点续传及秒传* param file*/function 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;let chunks Math.ceil(file.size / chunkSize);let spark new SparkMD5.ArrayBuffer();loadNext();fileReader.onload (e {spark.append(e.target.result);if (currentChunk chunks) {currentChunk;loadNext();// console.log(校验MD5 ((currentChunk / chunks) * 100).toFixed(0) %);$(#md5span).text( ((currentChunk / chunks) * 100).toFixed(0) %);} else {let md5 spark.end();console.log(MD5计算完毕${file.name} \nMD5${md5} \n分片${chunks} 大小:${file.size} 用时${new Date().getTime() - time} ms);isUpload(file, md5);}});function loadNext() {let start currentChunk * chunkSize;let end ((start chunkSize) file.size) ? file.size : start chunkSize;fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));}}function isUpload(file, md5) {//Ajax提交$.ajax({url: http://localhost:8080/fileservice/file/upload?identifiermd5,type: GET,async: true, //异步processData: false, //很重要告诉jquery不要对form进行处理contentType: false, //很重要指定为false才能形成正确的Content-Typesuccess: function (data) {// 服务器分片校验函数秒传及断点续传基础if (data.data.skipUpload) {dataend new Date();$(#usetime).text(dataend.getTime() - databgein.getTime());$(#rate).text(100);$(#output).text(shardCount / shardCount);}else{repeatupload(file, md5, data.data.uploadedChunks);}}, error: function (XMLHttpRequest, textStatus, errorThrown) {alert(服务器出错!);}});}function repeatupload(file, filemd5, uploadedChunks) {size file.size; //总大小shardCount Math.ceil(size / chunkSize); //总片数for (var i 0; i shardCount; i) {var chunkNumber i1;if(uploadedChunks ! null uploadedChunks.indexOf(chunkNumber) 0){console.log(chunkNumber分片已存在);//如果分片存在就不用上传了uploadChunks(file.name, filemd5, size);continue;}upload(file, filemd5, uploadedChunks, chunkNumber);}}/**上传每一分片* file 文件对象* filemd5 整个文件的md5* date 文件第一个分片上传的日期(如:20170122)* i 文件第i个分片* type 1为检测2为上传*/function upload(file, filemd5, uploadedChunks, chunkNumber) {//计算每一片的起始与结束位置var start (chunkNumber-1) * chunkSize,end Math.min(size, start chunkSize);//构造一个表单FormData是HTML5新增的var form new FormData();//按大小切割文件段  var data file.slice(start, end);form.append(chunkNumber, chunkNumber); //文件块编号从1开始form.append(totalChunks, shardCount);form.append(identifier, filemd5);form.append(chunkSize, chunkSize);form.append(currentChunkSize, data.size);form.append(relativePath, file.name);form.append(filename, file.name);form.append(totalSize, size);form.append(total, shardCount); //总片数form.append(upfile, data); //Ajax提交$.ajax({url: http://localhost:8080/fileservice/file/upload,type: POST,data: form,async: true, //异步processData: false, //很重要告诉jquery不要对form进行处理contentType: false, //很重要指定为false才能形成正确的Content-Typesuccess: function (data) {uploadChunks(file.name, filemd5, size);}, error: function (XMLHttpRequest, textStatus, errorThrown) {alert(服务器出错!);}});}function uploadChunks(filename, identifier, totalSize) {//服务器返回分片是否上传成功succeed;//改变界面if(succeed shardCount){succeed shardCount;}$(#output).text(succeed / shardCount);if (succeed shardCount) {merge(filename, identifier, totalSize);dataend new Date();$(#usetime).text(dataend.getTime() - databgein.getTime());}//进度currentIndex;if(currentIndex shardCount){currentIndex shardCount;}$(#rate).text(((currentIndex / shardCount) * 100).toFixed(0));}function merge(filename, identifier, totalSize) {var json {filename:filename,identifier: identifier,totalSize: totalSize};//Ajax提交$.ajax({url: http://localhost:8080/fileservice/file/merge,type: POST,data: JSON.stringify(json),dataType: json,async: true, //异步contentType: application/json;charsetutf-8,success: function (data) {if (data.code ! 200) {alert(服务器出错!);}}, error: function (XMLHttpRequest, textStatus, errorThrown) {alert(服务器出错!);}});}/script /head bodyinput typefile idfile/ button idupload上传/button span stylefont-size:12px校验MD5: span idmd5span/span/span span stylefont-size:12px;margin-left:20px;等待: span idoutput/span/span span stylefont-size:12px;margin-left:20px;进度: span idrate/span%/span span stylefont-size:12px;margin-left:20px;用时: span idusetime/span/spanbr/ br/ br/ br/ button idquerybut查询/button h2文件列表/h2 tabletheadtrtdmd5/tdtd文件名/tdtd地址/td/tr/theadtbody idfilediv/tbody/table/body/html 总结 因为本文章只是简单示例仅供参考实际开发中可能会碰到一些问题我的原则就是来一个再解决一个绝不提前优化。 并发上传同一文件的情况可以由前端生成唯一id后端通过id和md5值组合的方式作为判断条件。upload接口分为上传文件块POST和检查文件块GET。多节点部署通过负载均衡访问的问题生成的文件可能会在任一节点上创建文件为了确保文件的唯一性和一致性找一台公共服务器或者第三方云服务目的是把负载均衡多个节点的文件统一到一起集中式管理。 源码地址https://download.csdn.net/download/u011974797/86862270
http://www.tj-hxxt.cn/news/131331.html

相关文章:

  • 静态淘宝网站制作模板建设网站一般要多钱
  • 网站建设售后服务费包括哪些有没有让人做问卷的网站
  • jsp网站一个公司的网站怎么做的
  • 建设网站时的常见故障分类上海比较好的设计院
  • 简述可口可乐公司的企业网站建设wordpress注册用户验证
  • 黄岩区建设局网站飞鱼ip代理
  • win2003建网站荣耀手机商城官方网站下载
  • 闲置物品交易网站怎么做的定制开发平台
  • 响应式网站怎么设置网址seo查询
  • 坪山新区网站建设建设银行公积金预约网站首页
  • 网站推广策划案哪里有百度应用商店
  • 公司网站维护好做吗百家号和网站同步做
  • 做网站教程免费重庆互联网公司排名
  • 国外 网页框架搭建的网站商丘网信办
  • 网站开发哪个好上海做网站hlanggroup
  • 甘肃省建设厅官方网站信息网移动网站开发视频怎样嵌入
  • 镇江网站排名优化价格徐州网站推广
  • 杭州工业设计公司排名前十强宁波seo怎么做引流推广
  • 专业做网文的网站百度反馈中心
  • 工作网站开发制作抚州网站开发机构
  • 美塔基500元做网站可信吗网站诊断方法
  • 网页建站要多久常用的网站推广的方式方法
  • 长沙网站制作首页国外做文化的网站
  • 口碑最好的旅游网站网站页面制作建议
  • 做网站怎么与客户谈判一个网站建设流程图
  • 苏州网站快速排名优化中国国家标准建设信息网站
  • 常见的电子商务网站有对网站开发流程的认识
  • 企业网站mp4怎么处理亳州公司做网站
  • 网站开发主管招聘jsp网站建设模板下载
  • html展示网站源代码想找可以在家做的手工活去什么网站