1、前端代码
<template>
<vxe-modal
v-model="show"
class="add-mark-dialog"
:title="title"
width="650"
height="600"
:show-footer="true"
destroy-on-close
@close="handleClose"
>
<div class="main-modal-body">
<div class="form-wrap">
<el-form ref="form" :model="form" :rules="rules" label-width="140px">
<el-row>
<el-col :span="24">
<el-form-item label="任务名称:" prop="task_name">
<el-input v-model="form.task_name" placeholder="请输入任务名称"></el-input>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item v-if="type == 'add'" label="上传文件:" prop="file_address">
<el-upload action :auto-upload="false" :show-file-list="false" :on-change="handleChange">
<div class="el-upload__text" style="color:#409EFF;"><i class="el-icon-upload"></i><em>点击上传</em></div>
<div class="el-upload__tip" slot="tip">只能上传zip格式文件,且大小不超过 10 GB 的视频</div>
</el-upload>
<div class="progress-box">
<!-- <span>上传进度:{{ percent.toFixed() }}%</span> -->
<el-progress :percentage="percent"></el-progress>
<!-- <el-button type="primary" size="mini" @click="handleClickBtn">{{ upload | btnTextFilter }}</el-button> -->
</div>
</el-form-item>
<el-form-item v-else-if="type == 'edit'" label="已上传文件:" prop="file_address">
<span style="word-break: break-all" v-for="(file, index) in imgUrlList" :key="index">{{
file.real_address
}}</span>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</div>
<template v-slot:footer>
<div class="a-r">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="btnLoading" :disabled="btnLoading">保存</el-button>
</div>
</template>
</vxe-modal>
</template>
<script>
import SparkMD5 from 'spark-md5'
import axios from 'axios'
import { mapState } from 'vuex'
import util from '@/libs/util'
import { upload, createImgMarkTask, updateImgMarkTask, chunkMerge } from '../api'
const defaultForm = {
task_name: ''
}
export default {
name: 'AddModal',
components: {},
props: {
},
filters: {
btnTextFilter(val) {
return val ? '暂停' : '继续'
}
},
data() {
return {
title: '新增',
type: 'add',
btnLoading: false,
show: false,
form: Object.assign({}, defaultForm),
detail: null,
tagarr: [],
addTagVisible: false,
typearr: [],
addTypeVisible: false,
inputValue: '',
inputValue1: '',
upHeaders: null,
fileList: [],
imgUrlList: [],
fileAddress: '',
rules: {
task_name: [
{ required: true, message: '请输入任务名称', trigger: 'blur' },
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
]
},
percent: 0,
upload: true,
percentCount: 0
}
},
computed: {
...mapState('admin', {
base_url: state => state.settings.base_url
})
},
watch: {},
methods: {
async handleChange(file) {
if (!file) return
this.percent = 0
this.fileAddress = ''
const fileObj = file.raw
let buffer
try {
buffer = await this.fileToBuffer(fileObj)
} catch (e) {
console.log(e)
}
const chunkSize = 2097152
const chunkList = []
const chunkListLength = Math.ceil(fileObj.size / chunkSize)
const suffix = /\.([0-9A-z]+)$/.exec(fileObj.name)[1]
const spark = new SparkMD5.ArrayBuffer()
spark.append(buffer)
const hash = spark.end()
let curChunk = 0
for (let i = 0; i < chunkListLength; i++) {
const item = {
chunk: fileObj.slice(curChunk, curChunk + chunkSize),
fileName: `${hash}_${i}.${suffix}`,
chunkNumber: i,
totalChunks: chunkListLength,
identifier: hash
}
curChunk += chunkSize
chunkList.push(item)
}
this.chunkList = chunkList
this.hash = hash
this.sendRequest()
},
sendRequest() {
const requestList = []
this.chunkList.forEach((item, index) => {
const fn = () => {
const formData = new FormData()
formData.append('chunk', item.chunk)
formData.append('fileName', item.fileName)
formData.append('chunkNumber', item.chunkNumber)
formData.append('totalChunks', item.totalChunks)
formData.append('identifier', item.identifier)
return axios({
baseURL: util.baseURL(),
url: '/api/img_mark_task/chunk_upload/',
method: 'post',
headers: { 'Content-Type': 'multipart/form-data', Authorization: 'JWT ' + util.cookies.get('token') },
data: formData
}).then(res => {
console.log('res=====1', res)
if (res.data.code === 2000) {
if (this.percentCount === 0) {
this.percentCount = 100 / this.chunkList.length
}
this.percent += this.percentCount
this.chunkList.splice(index, 1)
}
})
}
requestList.push(fn)
})
let i = 0
const complete = () => {
chunkMerge({ hash: this.hash, totalChunks: requestList.length }).then(res => {
console.log('res===', res)
this.fileAddress = res.data.url
})
}
const send = async () => {
if (!this.upload) return
if (i >= requestList.length) {
complete()
return
}
await requestList[i]()
i++
send()
}
send()
},
handleClickBtn() {
this.upload = !this.upload
if (this.upload) this.sendRequest()
},
fileToBuffer(file) {
return new Promise((resolve, reject) => {
const fr = new FileReader()
fr.onload = e => {
resolve(e.target.result)
}
fr.readAsArrayBuffer(file)
fr.onerror = () => {
reject(new Error('转换文件格式发生错误'))
}
})
},
handleSubmit() {
},
}
}
</script>
<style scoped lang="scss">
.main-modal-body {
.form-wrap {
padding: 16px 0 0 0;
}
}
</style>
<style>
.add-mark-dialog.vxe-modal--wrapper.type--modal .vxe-modal--body {
padding: 0;
}
</style>
2、后端代码
def deldir(dir):
if not os.path.exists(dir):
return False
if os.path.isfile(dir):
os.remove(dir)
return
for i in os.listdir(dir):
t = os.path.join(dir, i)
if os.path.isdir(t):
deldir(t)
else:
os.unlink(t)
os.removedirs(dir)
class ChunkUploadViewSet(CustomModelViewSet):
def chunk_upload(self, request, *args, **kwargs):
file_name = request.POST.get('identifier')
chunk_index = int(request.POST.get("chunkNumber"))
total_chunk = int(request.POST.get("totalChunks"))
upload_file = request.FILES["chunk"]
file_path = os.path.join(settings.MEDIA_ROOT, 'chunk_file', file_name)
chunk_path = os.path.join(file_path, str(chunk_index))
if not os.path.exists(file_path):
os.makedirs(file_path)
with open(chunk_path, 'wb+') as destination:
for chunk in upload_file.chunks():
destination.write(chunk)
res = {
'file_name': file_name,
"chunk_index": chunk_index,
'status': 1
}
return DetailResponse(data=res, msg="获取成功")
def chunk_merge(self, request, *args, **kwargs):
file_name = request.GET.get("hash")
total_chunk = int(request.GET.get("totalChunks"))
file_path = os.path.join(settings.MEDIA_ROOT, 'chunk_file', file_name)
chunks_list = list(set(os.listdir(file_path)))
is_over = False
if len(chunks_list) == total_chunk:
is_over = True
if is_over:
all_chunk = os.listdir(file_path)
all_chunk.sort(key=lambda x: int(x))
target_path = os.path.join(settings.MEDIA_ROOT, 'chunk_file', file_name+".zip")
target_path_temp = os.path.join(settings.MEDIA_ROOT, 'chunk_file', file_name+"temp")
with open(target_path, "wb+") as f:
for chunk in all_chunk:
chunk_path = os.path.join(file_path, chunk)
with open(chunk_path, "rb") as g:
data = g.read()
f.write(data)
deldir(file_path)
file_url = os.sep.join([settings.MEDIA_ROOT, 'chunk_file', file_name+".zip"])
res = {
"url": file_url,
"fileName": file_name,
}
return DetailResponse(data=res, msg="获取成功")