Compare commits

...

2 Commits

Author SHA1 Message Date
liuxiao
0487b52001 分片上传组件提交 2024-08-08 11:22:16 +08:00
liuxiao
7c07f7dfd0 分片上传修改 2024-08-07 15:54:09 +08:00
9 changed files with 379 additions and 37 deletions

View File

@ -98,40 +98,52 @@ public class SysOssController extends BaseController {
*/
@Log(title = "本地文件上传", businessType = BusinessType.INSERT)
@PostMapping("/addPartsUpload")
public R<Map<String, String>> addPartsUpload(@RequestPart("file") MultipartFile file, HttpServletRequest req) {
Map<String,String> map=new HashMap<>();
String realPath = req.getSession().getServletContext().getRealPath("/uploadFile/");
String format = sdf.format(new Date());
///www/wwwroot/guanwang/web/image/file/upload/
// String filePath=uploadPath+"/file/upload/";
File folder = new File(realPath + format);
if (!folder.isDirectory()){
folder.mkdirs();
}
String oldName = file.getOriginalFilename();
String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."), oldName.length());
public R<Map<String, String>> addPartsUpload(@RequestPart("file") MultipartFile file) {
Map<String, String> map = new HashMap<>();
String filePath = uploadPath + "/file/upload/";
try {
file.transferTo(new File(realPath,newName));
String filePath = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + "/uploadFile" + format + newName;
Long image = iSysImageService.uploadImage(newName,filePath);
map.put("url",filePath);
map.put("fileName",newName);
map.put("ossId",image.toString());
if (file.isEmpty()) {
return R.warn("文件为空");
}
String fileName = System.currentTimeMillis() + "-" + file.getOriginalFilename();
//文件上传的路径当前项目的根目录
System.err.println(filePath);
// 创建目标目录如果不存在
File directory = new File(filePath);
if (!directory.exists()) {
directory.mkdirs();
}
// 保存文件到目标目录
File uploadFile = new File(directory.getAbsolutePath() + File.separator + fileName);
file.transferTo(uploadFile);
//上传服务器地址
String pathFan = filePath.replace("\\", "/");
//filePath获取到的地址斜杠是 \ 单斜杠是特殊符号得用双斜杠代替得换成 / 才能访问到
//返回全路径图片地址
String returnPath = ("http://47.121.27.78:8008/upload/" + fileName).replace("\\", "/");
//修改数据库
Long image = iSysImageService.uploadImage(fileName, returnPath);
System.err.println("替换后:" + pathFan);
map.put("url", returnPath);
map.put("fileName", fileName);
map.put("ossId", image.toString());
return R.ok(map);
} catch (IOException ex) {
ex.printStackTrace();
} catch (IOException e) {
return R.warn("文件上传失败: " + e);
}
return R.warn("文件上传失败!");
}
/**
* 下载OSS对象
*
* @param ossId OSS对象ID
*/
/**
* 下载OSS对象
*
* @param ossId OSS对象ID
*/
@SaCheckPermission("system:oss:download")
@GetMapping("/download/{ossId}")
public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException {

View File

@ -49,6 +49,12 @@ public class GwIndex extends BaseEntity {
*/
private String titleEnglish;
/**
* 链接
*/
private String twLink;
/**
* 业务名称英文描述
*/

View File

@ -60,13 +60,11 @@ public class FileChunkFilelistBo extends BaseEntity {
/**
* 创建者id
*/
@NotNull(message = "创建者id不能为空", groups = { AddGroup.class, EditGroup.class })
private Long createUserId;
/**
* 更新者id
*/
@NotNull(message = "更新者id不能为空", groups = { AddGroup.class, EditGroup.class })
private Long updateUserId;

View File

@ -40,6 +40,11 @@ public class GwIndexBo extends BaseEntity {
*/
private String fileType;
/**
* 链接
*/
private String twLink;
/**
* 业务类型 0石油化工业务 1基建业务 2铁矿石业务 3燃气业务
*/

View File

@ -59,6 +59,11 @@ public class GwIndexVo {
@ExcelProperty(value = "业务名称")
private String businessName;
/**
* 链接
*/
private String twLink;
/**
* 图片标题英文描述
*/

View File

@ -310,7 +310,7 @@ public class FileChunkFilelistServiceImpl implements IFileChunkFilelistService {
}
} catch (IOException e) {
log.error("合并出现错误:" + e.getMessage(), e);
log.error("合并出现错误:" + e);
throw new RuntimeException("合并出现错误," + e.getMessage());
}
}

View File

@ -1,17 +1,327 @@
<template>
<div>
<el-upload>
<i class="el-icon-plus"></i>
</el-upload>
<div id="global-uploader">
<!-- 上传 -->
<uploader
ref="uploader"
:options="options"
:autoStart="false"
@file-added="onFileAdded"
@file-success="onFileSuccess"
@file-progress="onFileProgress"
@file-error="onFileError"
class="uploader-app">
<uploader-unsupport></uploader-unsupport>
<uploader-btn id="global-uploader-btn" :attrs="attrs" ref="uploadBtn">选择文件</uploader-btn>
<uploader-list v-show="panelShow">
<div class="file-panel" slot-scope="props" :class="{'collapse': collapse}">
<div class="file-title">
<h2>文件列表</h2>
<div class="operate">
<el-button @click="fileListShow" type="text" :title="collapse ? '展开':'折叠' ">
<i class="el-icon-d-caret" style="color:black;font-size: 18px"
:class="collapse ? 'inuc-fullscreen': 'inuc-minus-round'"></i>
</el-button>
<el-button @click="close" type="text" title="关闭">
<i class="el-icon-close" style="color:black;font-size: 18px"></i>
</el-button>
</div>
</div>
<ul class="file-list">
<li v-for="file in props.fileList" :key="file.id">
<uploader-file :class="'file_' + file.id" ref="files" :file="file" :list="true"></uploader-file>
</li>
<div class="no-file" v-if="!props.fileList.length"><i class="iconfont icon-empty-file"></i> 暂无待上传文件</div>
</ul>
</div>
</uploader-list>
</uploader>
</div>
</template>
<script>
export default {
/**
* 全局上传插件
* 调用方法Bus.$emit('openUploader', {}) 打开文件选择框参数为需要传递的额外参数
* 监听函数Bus.$on('fileAdded', fn); 文件选择后的回调
* Bus.$on('fileSuccess', fn); 文件上传成功的回调
*/
import { getToken } from '@/utils/auth'
import {ACCEPT_CONFIG} from '@/assets/js/config'
import Bus from '@/assets/js/bus'
import SparkMD5 from 'spark-md5';
import { fileMerge } from '@/api/system/file/fileuploader/fileuploader';
export default {
data() {
return {
options: {
// URL
target: process.env.VUE_APP_BASE_API +'/official/chunkFilelist/upload',
//
chunkSize: 5 * 1024 * 1000,
//file
fileParameterName: 'file',
//
//simultaneousUploads: 1,
//
maxChunkRetries: 0,
//
//chunkRetryInterval: 5000,
//
testChunks: true,
//
checkChunkUploadedByResponse: function (chunk, message) {
let objMessage = JSON.parse(message);
if (objMessage.skipUpload) {
return true;
}
return (objMessage.uploaded || []).indexOf(chunk.offset + 1) >= 0
},
headers: {
Authorization: 'Bearer ' + getToken()
},
//
query() {
}
},
attrs: {
accept: ACCEPT_CONFIG.getAll()
},
panelShow: false, //panel
collapse: false
}
},
mounted() {
//
Bus.$on('openUploader', query => {
this.params = query || {};
this.options.headers.Authorization = 'Bearer ' + query.token
if (this.$refs.uploadBtn) {
$("#global-uploader-btn").click();
}
});
},
computed: {
//Uploader
uploader() {
return this.$refs.uploader.uploader;
}
},
methods: {
onFileAdded(file) {
this.panelShow = true;
this.computeMD5(file);
Bus.$emit('fileAdded');
},
//file-progress
onFileProgress(rootFile, file, chunk) {
console.log(`上传中 ${file.name}chunk${chunk.startByte / 1024 / 1024} ~ ${chunk.endByte / 1024 / 1024}`)
},
onFileSuccess(rootFile, file, response, chunk) {
let res = JSON.parse(response);
// TODO :200Uploader
// if (!res.result) {
// this.$message({message: res.message, type: 'error'});
// //
// this.statusSet(file.id, 'failed');
// return
// }
//
if (res.data.needMerge) {
//
this.statusSet(file.id, 'merging');
let param = {
'filename': rootFile.name,
'identifier': rootFile.uniqueIdentifier,
'totalSize': rootFile.size
}
this.customMerge(param, file.id);
//
} else {
Bus.$emit('fileSuccess');
console.log('上传成功');
this.panelShow = false
Bus.$emit('refreshFileList'); //
}
},
customMerge (param, fileId, count = 3) {
if (count === 0) return;
fileMerge(param).then(res => {
//
Bus.$emit('fileSuccess');
this.statusRemove(fileId);
this.panelShow = false
Bus.$emit('refreshFileList'); //
}).catch(e => {
console.log("合并异常,重新发起请求,文件名为:", file.name)
//,线,,,
this.customMerge(params, fileId, count - 1)
});
},
onFileError(rootFile, file, response, chunk) {
this.$message({
message: response,
type: 'error'
})
},
/**
* 计算md5实现断点续传及秒传
* @param file
*/
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;
const chunkSize = 10 * 1024 * 1000;
let chunks = Math.ceil(file.size / chunkSize);
let spark = new SparkMD5.ArrayBuffer();
// "MD5"
this.statusSet(file.id, 'md5');
file.pause();
loadNext();
fileReader.onload = (e => {
spark.append(e.target.result);
if (currentChunk < chunks) {
currentChunk++;
loadNext();
// MD5
this.$nextTick(() => {
$(`.myStatus_${file.id}`).text('校验MD5 ' + ((currentChunk / chunks) * 100).toFixed(0) + '%')
})
} else {
let md5 = spark.end();
this.computeMD5Success(md5, file);
console.log(`MD5计算完毕${file.name} \nMD5${md5} \n分片${chunks} 大小:${file.size} 用时:${new Date().getTime() - time} ms`);
}
});
fileReader.onerror = function () {
this.error(`文件${file.name}读取出错,请检查该文件`)
file.cancel();
};
function loadNext() {
let start = currentChunk * chunkSize;
let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
}
},
computeMD5Success(md5, file) {
// uploaderopts
Object.assign(this.uploader.opts, {
query: {
...this.params,
filename: file.name
}
})
file.uniqueIdentifier = md5;
file.resume();
this.statusRemove(file.id);
},
fileListShow() {
let $list = $('#global-uploader .file-list');
if ($list.is(':visible')) {
$list.slideUp();
this.collapse = true;
} else {
$list.slideDown();
this.collapse = false;
}
},
close() {
this.uploader.cancel();
this.panelShow = false;
},
/**
* 新增的自定义的状态: 'md5''transcoding''failed'
* @param id
* @param status
*/
statusSet(id, status) {
let statusMap = {
md5: {
text: '校验MD5',
bgc: '#fff'
},
merging: {
text: '合并中',
bgc: '#e2eeff'
},
transcoding: {
text: '转码中',
bgc: '#e2eeff'
},
failed: {
text: '上传失败',
bgc: '#e2eeff'
}
}
this.$nextTick(() => {
$(`<p class="myStatus_${id}"></p>`).appendTo(`.file_${id} .uploader-file-status`).css({
'position': 'absolute',
'top': '0',
'left': '0',
'right': '0',
'bottom': '0',
'zIndex': '1',
'line-height': 'initial',
'backgroundColor': statusMap[status].bgc
}).text(statusMap[status].text);
})
},
statusRemove(id) {
this.$nextTick(() => {
$(`.myStatus_${id}`).remove();
})
},
error(msg) {
this.$notify({
title: '错误',
message: msg,
type: 'error',
duration: 2000
})
}
},
watch: {},
destroyed() {
Bus.$off('openUploader');
},
components: {}
}
</script>
<style>
</style>
<style scoped lang="scss">
</style>

View File

@ -1,6 +1,7 @@
import Vue from 'vue'
import Cookies from 'js-cookie'
import uploader from 'vue-simple-uploader'
import Element from 'element-ui'
import './assets/styles/element-variables.scss'
@ -72,6 +73,7 @@ Vue.component('FragmentUpload', FragmentUpload)
Vue.use(directive)
Vue.use(plugins)
Vue.use(VueMeta)
Vue.use(uploader)
DictData.install()
/**

View File

@ -158,6 +158,10 @@
<el-form-item label="标题名称" prop="titleName">
<el-input v-model="form.titleName" placeholder="请输入标题名称" />
</el-form-item>
<el-form-item label="分片上传" prop="titleName">
<FragmentUpload
:width=100 :height=100 />
</el-form-item>
<el-form-item label="标题英文名称" prop="titleEnglish">
<el-input v-model="form.titleEnglish" placeholder="请输入标题英文名称" />
</el-form-item>