添加MD5组件,修改切片上传

This commit is contained in:
RenCheng 2025-02-20 17:22:54 +08:00
parent 0c97e64765
commit 61c286349c
10 changed files with 1046 additions and 209 deletions

View File

@ -31,6 +31,7 @@
"md5": "^2.2.1",
"npm": "^6.14.11",
"nprogress": "^0.2.0",
"spark-md5": "^3.0.2",
"tinymce": "^5.3.2",
"viser-vue": "^2.4.8",
"vue": "^2.6.10",
@ -56,13 +57,13 @@
"@vue/cli-service": "^3.3.0",
"@vue/eslint-config-standard": "^4.0.0",
"babel-eslint": "7.2.3",
"compression-webpack-plugin": "^3.1.0",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.1.0",
"html-webpack-plugin": "^4.2.0",
"less": "^3.9.0",
"less-loader": "^4.1.0",
"vue-template-compiler": "^2.6.10",
"html-webpack-plugin": "^4.2.0",
"compression-webpack-plugin": "^3.1.0"
"vue-template-compiler": "^2.6.10"
},
"eslintConfig": {
"root": true,

View File

@ -2,11 +2,11 @@ import { getAction, deleteAction, putAction, postAction, httpAction,uploadAction
const getFileLinkInfo = (params)=>getAction("/fileDataLink/getFileLinkInfo",params);
const processFile = (params)=>postAction("/fileDataLink/processFile",params);
const fileDataLinkDelete = (params)=>deleteAction("/fileDataLink/delete",params);
export {
getFileLinkInfo,
processFile
processFile,
fileDataLinkDelete
}

View File

@ -142,6 +142,35 @@ export function downloadFile(url, fileName, parameter) {
})
}
/**
* 下载文件
* @param url 文件路径
* @param fileName 文件名
* @param parameter
* @returns {*}
*/
export function downloadFile2 (fileName, parameter, downloadUrl) {
return downFile(downloadUrl || fileApi.FileDown, parameter).then((data) => {
if (!data || data.size === 0) {
message.error('文件不存在,请重新上传文件')
return
}
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(new Blob([data]), fileName)
} else {
const url = window.URL.createObjectURL(new Blob([data]))
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName)
document.body.appendChild(link)
link.click()
document.body.removeChild(link) // 下载完成移除元素
window.URL.revokeObjectURL(url) // 释放掉blob对象
}
})
}
/**
* 文件上传 用于富文本上传图片
* @param url

View File

@ -0,0 +1,440 @@
<template>
<div>
<a-row type="flex">
<a-col flex="auto">
<a-upload
class="upload-wrapper"
:showUploadList="false"
:disabled="maxFile && tableDate.length >= maxFile"
:accept="accept"
:before-upload="beforeUpload"
:customRequest="customRequestUpload"
:multiple="true"
@change="handleChange">
<!-- <a-input style="width:100%" :value="fileList.map(item=> item.name).join('、')" /> -->
<a-button :disabled="maxFile && tableDate.length >= maxFile">
<a-icon type="upload" /> 文档上传
</a-button>
</a-upload>
</a-col>
<a-col flex="100px" v-if="false" >
<a-button @click="customRequestUpload">
<a-icon type="upload" /> 文档上传
</a-button>
</a-col>
</a-row>
<div class="result-wrapper" :style="{minHeight: boxHeight + 'px'}" >
<div class="item" v-for="(file,idx) in tableDate" :key="file.uid">
<div class="content">
<div class="body">
<div class="fileName"> {{ file.fileName }}</div>
<div>
<a-space>
<a-button v-if="canDownload" type="link" style="padding: 0" @click="handleDownload(file)">下载</a-button>
<a-popconfirm
title="是否确认删除该文件?"
@confirm="handleDelete(file, idx)"
>
<a-icon style="margin-left:10px; cursor: pointer;" type="delete" />
</a-popconfirm>
</a-space>
</div>
<a-progress
class="progress"
v-if="autoUpload"
:percent="file.percentage"
:strokeWidth="3"
:showInfo="false" />
</div>
</div>
<slot name="extra" :idx="idx" />
</div>
</div>
</div>
</template>
<script>
import SparkMD5 from 'spark-md5'
import { postAction,downloadFile2 } from '@/api/manage'
import { verifyFileExist } from '@/api/fileapi'
export default {
name: 'SliceUpload',
props: {
boxHeight: {
type: Number,
default: 300
},
// eslint-disable-next-line vue/require-default-prop
maxFile: Number,
accept: {
type: String,
default: '.jpg,.png,.doc,.docx,.pdf,.txt,.jpeg'
},
autoUpload: { //
type: Boolean,
default: true
},
canRepeat: { //
type: Boolean,
default: true
},
canDownload: { //
type: Boolean,
default: false
},
dataLinkType: {
type: String,
default: '1'
}
},
data () {
return {
fileMD5: {},
isStop: false,
fileList: [],
tableDate: [],
}
},
methods: {
stop (record) {
this.isStop = true
record.uploadStatus = 0
},
start (record, index) {
const file = this.fileList[index].originFileObj
const currentRow = this.tableDate.find((row) => row.uid === file.uid)
this.isStop = false
record.uploadStatus = 1
this.uploadByPieces({
file, //
currentRow,
success: (data) => {
record.percentage = 100
},
error: (e) => {
record.percentage = 0
}
})
},
//
setFileList (fileList) {
this.tableDate = fileList
},
deleteFile () {
this.fileList = []
this.tableDate = []
},
getFileList () {
return this.tableDate
},
bytesToSize (bytes) {
if (bytes === 0) return '0 B'
const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i]
},
handleDelete (file, idx) {
this.tableDate.splice(idx, 1)
delete this.fileMD5[file.uid]
},
async beforeUpload (file) {
if (!this.canRepeat) {
const md5 = await this.getMd5(file, file.size)
const find = Object.values(this.fileMD5).find(v => {
return v === md5
})
if (!this.canRepeat && find) {
this.$message.warning(`文件重复,重复的文件为:${file.name}`)
throw new Error('file repeat')
} else {
this.fileMD5[file.uid] = md5
}
}
this.fileList.push(file)
// uploadStatus 1 0 2
this.tableDate.push({
fileData: file,
uid: file.uid,
fileName: file.name,
size: file.size,
type: file.type,
percentage: 0,
uploadStatus: 1,
remarks: ''
})
if (!this.autoUpload) {
throw new Error('not auto upload')
}
},
/**
* 自定义上传事件
*/
customRequestUpload ({ file }) {
//
const currentRow = this.tableDate.find((row) => row.uid === file.uid)
if (currentRow) {
// 0
currentRow.percentage = 0
const _20M = 20 * 1024 * 1024
if (file.size > _20M) { // 20M
this.uploadByPieces({ //
file, //
currentRow,
success: (data) => {
currentRow.percentage = 100
},
error: (e) => {
currentRow.percentage = 0
}
})
} else { // 20M
this.uploadDirectly(currentRow, file)
}
}
},
getMd5 (file, chunkSize) {
return new Promise((resolve, reject) => {
const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
const chunks = Math.ceil(file.size / chunkSize)
let currentChunk = 0
const spark = new SparkMD5.ArrayBuffer() //
const fileReader = new FileReader() //
fileReader.onload = function (e) {
spark.append(e.target.result)
currentChunk++
if (currentChunk < chunks) {
loadNext()
} else {
const md5 = spark.end() // md5
resolve(md5)
}
}
fileReader.onerror = function (e) {
reject(e)
}
function loadNext () {
const start = currentChunk * chunkSize
let end = start + chunkSize
end > file.size && (end = file.size)
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
}
loadNext()
})
},
buildFileFormData (fileName, fileSize, md5Value, shareTotal, shareIndex, file, fileShare) {
const formData = new FormData()
formData.append('fileName', fileName)
formData.append('fileSize', fileSize)
formData.append('fileExt', fileName.substring(fileName.lastIndexOf('.')))
formData.append('md5Value', md5Value)
formData.append('shareTotal', shareTotal)
formData.append('shareIndex', shareIndex)
formData.append('file', file)
formData.append('fileShare', fileShare)
formData.append("dataLinkType",this.dataLinkType);
return formData
},
/**
* 直接上传
* @param {File} file file文件
*/
async uploadDirectly (currentRow, file) {
let fileMD5Value = this.fileMD5[file.uid]
if (!fileMD5Value) {
fileMD5Value = await this.getMd5(file, file.size)
}
try {
const res = await verifyFileExist({ fileMD5Value })
if (res.exist) { //
currentRow.percentage = 100
currentRow.uploadStatus = 2
currentRow.result = res
} else { //
const formData = this.buildFileFormData(file.name, file.size, fileMD5Value, 0, 0, file, false)
const url = '/fileDataLink/uploadFile'
try {
const res = await postAction(url, formData)
if (res) {
currentRow.percentage = 100
currentRow.uploadStatus = 2
currentRow.result = {
fileId: res.id
}
} else {
currentRow.percentage = 0
currentRow.uploadStatus = 1
}
} catch (error) {
console.error(error)
}
}
} catch (error) {
console.error(error)
this.$message.error('获取文件是否已上传状态失败')
}
},
//
uploadByPieces ({ file, currentRow, success, error }) {
// const that = this
//
var slicingSize = null
if (file.size <= 20971520) {
// 20M2MB
slicingSize = 2 * 1024 * 1024 // MB
} else if (file.size <= 524288000) {
// 500M5MB
slicingSize = 5 * 1024 * 1024 // MB
} else {
// 500M10MB
slicingSize = 10 * 1024 * 1024 // MB
}
const sumSlicingCount = Math.ceil(file.size / slicingSize) //
currentRow.remarks = '正在获取hash值...'
this.getMd5(file, slicingSize)
.then((res) => {
this.fileMD5[file.uid] = res
currentRow.remarks = ''
this.readFileMD5(file, currentRow, slicingSize, sumSlicingCount, success, error)
})
.catch((e) => {
console.error('MD5计算错误', e)
})
},
// file currentIndex slicingSize
getSlicingInfo (file, currentIndex, slicingSize) {
const start = currentIndex * slicingSize
const end = Math.min(file.size, start + slicingSize)
const slicingInfo = file.slice(start, end)
return slicingInfo
},
//
readFileMD5 (file, currentRow, slicingSize, sumSlicingCount, success, error) {
//
verifyFileExist({ fileMD5Value: this.fileMD5[file.uid] })
.then((res) => {
const { exist, shareTotal, shareIndex } = res
if (exist) {
if (shareIndex === (shareTotal - 1)) { //
currentRow.percentage = 100
currentRow.uploadStatus = 2
currentRow.result = res
} else { //
const pross = (shareIndex + 1 / sumSlicingCount) * 100 //
currentRow.percentage = Number(pross.toFixed(2))
this.uploadSliceFile(file, currentRow, slicingSize, sumSlicingCount, shareIndex + 1, success, error)
}
} else { // 0
this.uploadSliceFile(file, currentRow, slicingSize, sumSlicingCount, 0, success, error)
}
})
.catch((e) => {
error && error(e)
})
},
/**
* 对切片文件进行上传
* @param {File} file
*/
uploadSliceFile (file, currentRow, slicingSize, sumSlicingCount, currIndex, success, error) {
if (currIndex < sumSlicingCount && !this.isStop) {
//
const currentInfo = this.getSlicingInfo(file, currIndex, slicingSize)
const result = new File([currentInfo], currIndex, { type: file.type, lastModified: Date.now() })
const formData = this.buildFileFormData(file.name, file.size, this.fileMD5[file.uid], sumSlicingCount, currIndex, result, true)
//
const url = '/fileDataLink/uploadFile'
postAction(url, formData).then((res) => {
const { completed, id } = res
if (completed) { //
currentRow.percentage = 100
currentRow.uploadStatus = 2
currentRow.result = {
fileId: id
}
} else {
//
const pross = ((currIndex + 1) / sumSlicingCount) * 100
currentRow.percentage = Number(pross.toFixed(2))
this.uploadSliceFile(file, currentRow, slicingSize, sumSlicingCount, currIndex + 1, success, error)
}
})
}
},
handleChange (info) {
this.fileList = [...info.fileList]
},
/**
* 下载文件
*/
handleDownload ({ fileName: filterWordName, filePath: filterWordPath }) {
downloadFile2(filterWordName, { filterWordName, filterWordPath }, '/file/verifyFileExist')
}
}
}
</script>
<style lang="less" scoped>
.upload-wrapper{
display: inline-block;
width: calc(100% - 10px);
/deep/.ant-upload{
width: 100%;
}
}
.result-wrapper {
margin-top: 10px;
width: 100%;
background: #FFFFFF;
border: 1px solid #D9DADB;
padding: 10px;
.item {
display: flex;
justify-content: space-between;
font-size: 14px;
font-weight: 400;
color: #666666;
line-height: 36px;
.content {
flex: 1;
.body {
position: relative;
justify-content: space-between;
padding-bottom: 4px;
display: flex;
.fileName {
max-width: 400px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.progress {
position: absolute;
left: 0;
bottom: 0px;
}
}
}
}
}
</style>

View File

@ -10,19 +10,14 @@
cancelText="关闭">
<a-spin :spinning="confirmLoading">
<a-upload
directory
v-model:fileList="fileList"
:customRequest="customRequest"
:beforeUpload="beforeUpload"
@change="handleChange"
:multiple="true"
>
<a-button>
<upload-outlined></upload-outlined>
选择文件夹上传.txt/.csv
</a-button>
</a-upload>
<SliceUpload
ref="technicalUpload"
:box-height="50"
:max-file="9999"
:can-download="false"
accept=".csv,.txt"
:dataLinkType='dataLinkType'
/>
</a-spin>
</a-modal>
</template>
@ -31,96 +26,32 @@
import moment from "moment"
import JCron from "@/components/jeecg/JCron";
import JSelectMultiple from '@/components/jeecg/JSelectMultiple'
import pick from 'lodash.pick'
import SliceUpload from './SliceUpload'
export default {
name: "fileDataLinkModal",
name: "uploadModal",
components: {
JCron,
JSelectMultiple
JSelectMultiple,
SliceUpload
},
data () {
return {
title:"操作",
buttonStyle: 'solid',
visible: false,
dateFormat:'YYYY-MM-DD HH:mm',
status:'',
model: {},
confirmLoading: false,
form: this.$form.createForm(this),
strategys:[],
dataStrategySelected:'',
idcDataStrategyId:"",
shipMode:{},
shipNum:{},
rangeTime:[],
queryParam: {
pageNum :1,
pageSize:20
},
labelCol: {
xs: { span: 24 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
dataLinkType:''
}
},
created () {
this.loadData();
},
methods: {
loadData() {
shipModelPageList(this.queryParam).then((res) => {
if (res.success) {
this.shipMode = res.result.rows
} else {
this.$message.warning(res.message);
}
});
shipNumPageList(this.queryParam).then((res) => {
if (res.success) {
this.shipNum = res.result.rows
} else {
this.$message.warning(res.message);
}
});
},
filterOption(input, option) {
return (
option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
);
},
selectModel(value){
shipNumQueryByModelId({modelId:value}).then((res) => {
if (res.success) {
this.shipNum = res.result
} else {
this.$message.warning(res.message);
}
});
},
add () {
this.rangeTime=[];
this.edit({});
},
edit (record) {
this.visible =true;
let that = this;
that.form.resetFields();
this.model = Object.assign({},record);
this.rangeTime.push(moment(this.model.startTime),moment(this.model.endTime))
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model,'name','describe','shipModelId','shipNumId'));
});
},
onChange(value, dateString) {
this.model.startDate = dateString[0];
this.model.endDate = dateString[1];
},
onOk(value) {
this.model.startDate = moment(value[0],dateFormat);
@ -131,45 +62,7 @@ import pick from 'lodash.pick'
this.visible = false;
},
handleOk () {
const that = this;
//
this.form.validateFields((err, values) => {
console.log('values',values)
if (!err) {
that.confirmLoading = true;
if(!this.model.id){
this.model.name = values.name;
this.model.shipModelId = values.shipModelId;
this.model.shipNumId = values.shipNumId;
this.model.describe = values.describe;
taskCreate(this.model).then((res)=>{
if(res.success){
that.$message.success(res.message);
that.$emit('ok');
}else{
that.$message.warning(res.message);
}
}).finally(() => {
that.confirmLoading = false;
that.close();
})
}else{
taskUpdateById({id:this.model.id,name:values.name,describe:values.describe,shipModelId:values.shipModelId,shipNumId:values.shipNumId,
startTime:this.model.startTime,endTime:this.model.endTime
}).then((res)=>{
if(res.success){
that.$message.success(res.message);
that.$emit('ok');
}else{
that.$message.warning(res.message);
}
}).finally(() => {
that.confirmLoading = false;
that.close();
})
}
}
})
this.close();
},
handleCancel () {
this.close()

View File

@ -19,7 +19,7 @@
<a-col :md="6" :sm="24" >
<a-button type="primary" style="left: 10px" @click="loadData" icon="search">查询</a-button>
<a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px;left: 10px">重置</a-button>
<a-button type="primary" style="left: 10px" @click="handleAdd" icon="upload">上传</a-button>
<a-button type="primary" @click="handleAdd" icon="upload" style="margin-left: 8px;left: 10px">上传</a-button>
</a-col>
</span>
@ -60,19 +60,18 @@
<!-- table区域-end -->
<!-- 表单区域 -->
<uploadModal ref="modalForm" @ok="modalFormOk"></uploadModal>
<uploadModal ref="modalForm"></uploadModal>
</a-card>
</template>
<script>
import uploadModal from './modules/uploadModal'
import { getFileLinkInfo,
processFile } from '@/api/fileDataLink'
import { getFileLinkInfo,processFile,fileDataLinkDelete } from '@/api/fileDataLink'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import JEllipsis from "@/components/jeecg/JEllipsis";
import {deleteAction, postAction} from '@/api/manage'
export default {
name: "filelink",
name: "waterDocuments",
mixins:[JeecgListMixin],
components: {
uploadModal,
@ -159,7 +158,7 @@
loadData() {
getFileLinkInfo(this.queryParam).then((res) => {
if (res.success) {
this.dataSource = res.result.rows||res.result;
this.dataSource = res.result;
if(res.result.total)
{
this.ipagination.total = res.result.total;
@ -171,13 +170,13 @@
},
handleDelete: function (id) {
var that = this;
taskDeleteById({id: id}).then((res) => {
if (res.success) {
that.$message.success(res.message);
that.loadData();
} else {
that.$message.warning(res.message);
}
deleteAction("/fileDataLink/delete?id="+id,{}).then((res) => {
if (res.success) {
this.$message.success(res.message);
that.loadData();
} else {
this.$message.warning(res.message);
}
});
},
processFile: function (record) {
@ -191,20 +190,10 @@
}
});
},
handleDistribute:function (id) {
var that = this;
taskDistributeTask({id: id}).then((res) => {
if (res.success) {
that.$message.success(res.message);
that.loadData();
} else {
that.$message.warning(res.message);
}
});
},
handleAdd() {
this.$refs.modalForm.add();
this.$refs.modalForm.title = "文件上传";
this.$refs.modalForm.dataLinkType=this.queryParam.dataLinkType;
},
}
}

View File

@ -108,14 +108,10 @@
</a-form>
</a-col>
<a-col :span="12" style="text-align: right;">
<input
type="file"
@change="getUpFileData" style="display:none;" id="file">
<a-button type="primary" style="margin-left: 8px" @click="clickfile">
<a-icon type="upload"/>
文件上传
</a-button>
</input>
<a-button type="primary" style="margin-left: 8px" @click="clickfile">
<a-icon type="upload"/>
文件上传
</a-button>
</a-col>
</a-row>
</div>
@ -134,14 +130,19 @@
<a-modal v-model:open="isopen" :title="modalTitle" ok-text="确认" cancel-text="取消" @ok="hideModal">
<a-input placeholder="请输入" v-model="dirName" style="color: #000000 !important"></a-input>
</a-modal>
<uploadModal ref="modalForm"></uploadModal>
</div>
</template>
<script>
import uploadModal from './modules/uploadModal'
import guaz from '@assets/guaz.png'
import {createDir,logicDelDir,renameDir,dirtreeList} from '@/api/dirapi'
import {downloadFile,filePreview,logicDeleteFile,pageList,uoloadFile} from '@/api/fileapi'
export default {
name: "fileManage",
components: {
uploadModal
},
data() {
return {
dirName:"",
@ -230,37 +231,17 @@ export default {
this.getDirList();
},
computed: {
uploadAction() {
return window._CONFIG['domianURL'] + "/file/uoloadFile";
},
},
methods: {
clickfile(){
document.getElementById("file").click();
},
getUpFileData(e){
if(this.selectId == "0"){
this.$message.warning("必须选择查询目录");
this.$message.warning("必须选择文件上传目录");
return;
}
let formData = new FormData()
const file = e.target.files[0];
formData.append('dirId', this.selectId);
formData.append('fileShare', false);
formData.append("fileName",document.getElementById("file").files[0].name);
formData.append("fileSuffix",'.'+(document.getElementById("file").files[0].name.split('.').slice(-1)[0]));
formData.append("fileSize",(document.getElementById("file").files[0].size/1024).toFixed(2));
formData.append("md5Value",Date.now());
formData.append("shareTotal","0");
formData.append("shareIndex","0");
formData.append("currShareM5","0");
formData.append("file",e.target.files[0]);
uoloadFile(formData).then((res) => {
if (res.success) {
this.searchgetFileList();
}
this.$message.warning(res.message);
})
this.$refs.modalForm.add();
this.$refs.modalForm.title = "文件上传";
this.$refs.modalForm.dirId = this.searchFile.dirId;
},
download(item){
let apiBaseUrl = window._CONFIG['domianURL'] || "/jeecg-boot";

View File

@ -0,0 +1,440 @@
<template>
<div>
<a-row type="flex">
<a-col flex="auto">
<a-upload
class="upload-wrapper"
:showUploadList="false"
:disabled="maxFile && tableDate.length >= maxFile"
:accept="accept"
:before-upload="beforeUpload"
:customRequest="customRequestUpload"
:multiple="true"
@change="handleChange">
<!-- <a-input style="width:100%" :value="fileList.map(item=> item.name).join('、')" /> -->
<a-button :disabled="maxFile && tableDate.length >= maxFile">
<a-icon type="upload" /> 文档上传
</a-button>
</a-upload>
</a-col>
<a-col flex="100px" v-if="false" >
<a-button @click="customRequestUpload">
<a-icon type="upload" /> 文档上传
</a-button>
</a-col>
</a-row>
<div class="result-wrapper" :style="{minHeight: boxHeight + 'px'}" >
<div class="item" v-for="(file,idx) in tableDate" :key="file.uid">
<div class="content">
<div class="body">
<div class="fileName"> {{ file.fileName }}</div>
<div>
<a-space>
<a-button v-if="canDownload" type="link" style="padding: 0" @click="handleDownload(file)">下载</a-button>
<a-popconfirm
title="是否确认删除该文件?"
@confirm="handleDelete(file, idx)"
>
<a-icon style="margin-left:10px; cursor: pointer;" type="delete" />
</a-popconfirm>
</a-space>
</div>
<a-progress
class="progress"
v-if="autoUpload"
:percent="file.percentage"
:strokeWidth="3"
:showInfo="false" />
</div>
</div>
<slot name="extra" :idx="idx" />
</div>
</div>
</div>
</template>
<script>
import SparkMD5 from 'spark-md5'
import { postAction,downloadFile2 } from '@/api/manage'
import { verifyFileExist } from '@/api/fileapi'
export default {
name: 'SliceUpload',
props: {
boxHeight: {
type: Number,
default: 300
},
// eslint-disable-next-line vue/require-default-prop
maxFile: Number,
accept: {
type: String,
default: '.jpg,.png,.doc,.docx,.pdf,.txt,.jpeg'
},
autoUpload: { //
type: Boolean,
default: true
},
canRepeat: { //
type: Boolean,
default: true
},
canDownload: { //
type: Boolean,
default: false
},
dirId: { //
type: String,
default: ''
}
},
data () {
return {
fileMD5: {},
isStop: false,
fileList: [],
tableDate: [],
}
},
methods: {
stop (record) {
this.isStop = true
record.uploadStatus = 0
},
start (record, index) {
const file = this.fileList[index].originFileObj
const currentRow = this.tableDate.find((row) => row.uid === file.uid)
this.isStop = false
record.uploadStatus = 1
this.uploadByPieces({
file, //
currentRow,
success: (data) => {
record.percentage = 100
},
error: (e) => {
record.percentage = 0
}
})
},
//
setFileList (fileList) {
this.tableDate = fileList
},
deleteFile () {
this.fileList = []
this.tableDate = []
},
getFileList () {
return this.tableDate
},
bytesToSize (bytes) {
if (bytes === 0) return '0 B'
const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i]
},
handleDelete (file, idx) {
this.tableDate.splice(idx, 1)
delete this.fileMD5[file.uid]
},
async beforeUpload (file) {
if (!this.canRepeat) {
const md5 = await this.getMd5(file, file.size)
const find = Object.values(this.fileMD5).find(v => {
return v === md5
})
if (!this.canRepeat && find) {
this.$message.warning(`文件重复,重复的文件为:${file.name}`)
throw new Error('file repeat')
} else {
this.fileMD5[file.uid] = md5
}
}
this.fileList.push(file)
// uploadStatus 1 0 2
this.tableDate.push({
fileData: file,
uid: file.uid,
fileName: file.name,
size: file.size,
type: file.type,
percentage: 0,
uploadStatus: 1,
remarks: ''
})
if (!this.autoUpload) {
throw new Error('not auto upload')
}
},
/**
* 自定义上传事件
*/
customRequestUpload ({ file }) {
//
const currentRow = this.tableDate.find((row) => row.uid === file.uid)
if (currentRow) {
// 0
currentRow.percentage = 0
const _20M = 20 * 1024 * 1024
if (file.size > _20M) { // 20M
this.uploadByPieces({ //
file, //
currentRow,
success: (data) => {
currentRow.percentage = 100
},
error: (e) => {
currentRow.percentage = 0
}
})
} else { // 20M
this.uploadDirectly(currentRow, file)
}
}
},
getMd5 (file, chunkSize) {
return new Promise((resolve, reject) => {
const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
const chunks = Math.ceil(file.size / chunkSize)
let currentChunk = 0
const spark = new SparkMD5.ArrayBuffer() //
const fileReader = new FileReader() //
fileReader.onload = function (e) {
spark.append(e.target.result)
currentChunk++
if (currentChunk < chunks) {
loadNext()
} else {
const md5 = spark.end() // md5
resolve(md5)
}
}
fileReader.onerror = function (e) {
reject(e)
}
function loadNext () {
const start = currentChunk * chunkSize
let end = start + chunkSize
end > file.size && (end = file.size)
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
}
loadNext()
})
},
buildFileFormData (fileName, fileSize, md5Value, shareTotal, shareIndex, file, fileShare) {
const formData = new FormData()
formData.append('fileName', fileName)
formData.append('fileSize', fileSize)
formData.append('fileExt', fileName.substring(fileName.lastIndexOf('.')))
formData.append('md5Value', md5Value)
formData.append('shareTotal', shareTotal)
formData.append('shareIndex', shareIndex)
formData.append('file', file)
formData.append('fileShare', fileShare)
formData.append('dirId', this.dirId)
return formData
},
/**
* 直接上传
* @param {File} file file文件
*/
async uploadDirectly (currentRow, file) {
let fileMD5Value = this.fileMD5[file.uid]
if (!fileMD5Value) {
fileMD5Value = await this.getMd5(file, file.size)
}
try {
const res = await verifyFileExist({ fileMD5Value })
if (res.exist) { //
currentRow.percentage = 100
currentRow.uploadStatus = 2
currentRow.result = res
} else { //
const formData = this.buildFileFormData(file.name, file.size, fileMD5Value, 0, 0, file, false)
const url = '/file/uoloadFile'
try {
const res = await postAction(url, formData)
if (res) {
currentRow.percentage = 100
currentRow.uploadStatus = 2
currentRow.result = {
fileId: res.id
}
} else {
currentRow.percentage = 0
currentRow.uploadStatus = 1
}
} catch (error) {
console.error(error)
}
}
} catch (error) {
console.error(error)
this.$message.error('获取文件是否已上传状态失败')
}
},
//
uploadByPieces ({ file, currentRow, success, error }) {
// const that = this
//
var slicingSize = null
if (file.size <= 20971520) {
// 20M2MB
slicingSize = 2 * 1024 * 1024 // MB
} else if (file.size <= 524288000) {
// 500M5MB
slicingSize = 5 * 1024 * 1024 // MB
} else {
// 500M10MB
slicingSize = 10 * 1024 * 1024 // MB
}
const sumSlicingCount = Math.ceil(file.size / slicingSize) //
currentRow.remarks = '正在获取hash值...'
this.getMd5(file, slicingSize)
.then((res) => {
this.fileMD5[file.uid] = res
currentRow.remarks = ''
this.readFileMD5(file, currentRow, slicingSize, sumSlicingCount, success, error)
})
.catch((e) => {
console.error('MD5计算错误', e)
})
},
// file currentIndex slicingSize
getSlicingInfo (file, currentIndex, slicingSize) {
const start = currentIndex * slicingSize
const end = Math.min(file.size, start + slicingSize)
const slicingInfo = file.slice(start, end)
return slicingInfo
},
//
readFileMD5 (file, currentRow, slicingSize, sumSlicingCount, success, error) {
//
verifyFileExist({ fileMD5Value: this.fileMD5[file.uid] })
.then((res) => {
const { exist, shareTotal, shareIndex } = res
if (exist) {
if (shareIndex === (shareTotal - 1)) { //
currentRow.percentage = 100
currentRow.uploadStatus = 2
currentRow.result = res
} else { //
const pross = (shareIndex + 1 / sumSlicingCount) * 100 //
currentRow.percentage = Number(pross.toFixed(2))
this.uploadSliceFile(file, currentRow, slicingSize, sumSlicingCount, shareIndex + 1, success, error)
}
} else { // 0
this.uploadSliceFile(file, currentRow, slicingSize, sumSlicingCount, 0, success, error)
}
})
.catch((e) => {
error && error(e)
})
},
/**
* 对切片文件进行上传
* @param {File} file
*/
uploadSliceFile (file, currentRow, slicingSize, sumSlicingCount, currIndex, success, error) {
if (currIndex < sumSlicingCount && !this.isStop) {
//
const currentInfo = this.getSlicingInfo(file, currIndex, slicingSize)
const result = new File([currentInfo], currIndex, { type: file.type, lastModified: Date.now() })
const formData = this.buildFileFormData(file.name, file.size, this.fileMD5[file.uid], sumSlicingCount, currIndex, result, true)
//
const url = '/file/uoloadFile'
postAction(url, formData).then((res) => {
const { completed, id } = res
if (completed) { //
currentRow.percentage = 100
currentRow.uploadStatus = 2
currentRow.result = {
fileId: id
}
} else {
//
const pross = ((currIndex + 1) / sumSlicingCount) * 100
currentRow.percentage = Number(pross.toFixed(2))
this.uploadSliceFile(file, currentRow, slicingSize, sumSlicingCount, currIndex + 1, success, error)
}
})
}
},
handleChange (info) {
this.fileList = [...info.fileList]
},
/**
* 下载文件
*/
handleDownload ({ fileName: filterWordName, filePath: filterWordPath }) {
downloadFile2(filterWordName, { filterWordName, filterWordPath }, '/file/verifyFileExist')
}
}
}
</script>
<style lang="less" scoped>
.upload-wrapper{
display: inline-block;
width: calc(100% - 10px);
/deep/.ant-upload{
width: 100%;
}
}
.result-wrapper {
margin-top: 10px;
width: 100%;
background: #FFFFFF;
border: 1px solid #D9DADB;
padding: 10px;
.item {
display: flex;
justify-content: space-between;
font-size: 14px;
font-weight: 400;
color: #666666;
line-height: 36px;
.content {
flex: 1;
.body {
position: relative;
justify-content: space-between;
padding-bottom: 4px;
display: flex;
.fileName {
max-width: 400px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.progress {
position: absolute;
left: 0;
bottom: 0px;
}
}
}
}
}
</style>

View File

@ -0,0 +1,76 @@
<template>
<a-modal wrap
:title="title"
:width="800"
:visible="visible"
:confirmLoading="confirmLoading"
@ok="handleOk"
okText="保存">
<a-spin :spinning="confirmLoading">
<SliceUpload
ref="technicalUpload"
:box-height="50"
:max-file="9999"
:can-download="false"
accept=".docx,.doc,.png,jpg,.pdf,.mp4"
:dirId="dirId"
/>
</a-spin>
</a-modal>
</template>
<script>
import moment from "moment"
import JCron from "@/components/jeecg/JCron";
import JSelectMultiple from '@/components/jeecg/JSelectMultiple'
import SliceUpload from './SliceUpload'
export default {
name: "uploadModal",
components: {
JCron,
JSelectMultiple,
SliceUpload
},
data () {
return {
title:"操作",
buttonStyle: 'solid',
visible: false,
confirmLoading: false,
strategys:[],
dirId:""
}
},
created () {
},
methods: {
add () {
this.visible =true;
},
onOk(value) {
this.model.startDate = moment(value[0],dateFormat);
this.model.endDate = moment(value[1],dateFormat);
},
close () {
this.$emit('close');
this.visible = false;
},
handleOk () {
this.close();
},
handleCancel () {
this.close()
},
}
}
</script>
<style scoped>
.disabled{
pointer-events: none;
}
</style>

View File

@ -4489,7 +4489,7 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
dependencies:
ms "2.1.2"
debuglog@*, debuglog@^1.0.1:
debuglog@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=
@ -6818,7 +6818,7 @@ import-local@^2.0.0:
pkg-dir "^3.0.0"
resolve-cwd "^2.0.0"
imurmurhash@*, imurmurhash@^0.1.4:
imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
@ -7956,11 +7956,6 @@ lodash._basecopy@^3.0.0:
resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36"
integrity sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=
lodash._baseindexof@*:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c"
integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw=
lodash._baseuniq@~4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8"
@ -7969,16 +7964,11 @@ lodash._baseuniq@~4.6.0:
lodash._createset "~4.0.0"
lodash._root "~3.0.0"
lodash._bindcallback@*, lodash._bindcallback@^3.0.0:
lodash._bindcallback@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4=
lodash._cacheindexof@*:
version "3.0.2"
resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92"
integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=
lodash._createassigner@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11"
@ -7988,19 +7978,12 @@ lodash._createassigner@^3.0.0:
lodash._isiterateecall "^3.0.0"
lodash.restparam "^3.0.0"
lodash._createcache@*:
version "3.1.2"
resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093"
integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=
dependencies:
lodash._getnative "^3.0.0"
lodash._createset@~4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26"
integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=
lodash._getnative@*, lodash._getnative@^3.0.0:
lodash._getnative@^3.0.0:
version "3.9.1"
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=
@ -8111,7 +8094,7 @@ lodash.pick@^4.4.0:
resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=
lodash.restparam@*, lodash.restparam@^3.0.0:
lodash.restparam@^3.0.0:
version "3.6.1"
resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=
@ -11734,6 +11717,11 @@ source-map@^0.7.3:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
spark-md5@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.2.tgz#7952c4a30784347abcee73268e473b9c0167e3fc"
integrity sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==
spdx-correct@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"