|
|
@@ -4,22 +4,24 @@ |
|
|
|
id="dataset" |
|
|
|
class="dropzone" |
|
|
|
/> |
|
|
|
|
|
|
|
<p class="upload-info"> |
|
|
|
{{ file_status_text }} |
|
|
|
<span class="success">{{ status }}</span> |
|
|
|
</p> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
<script> |
|
|
|
/* eslint-disable eqeqeq */ |
|
|
|
// import Dropzone from 'dropzone/dist/dropzone.js'; |
|
|
|
// import 'dropzone/dist/dropzone.css' |
|
|
|
import SparkMD5 from 'spark-md5'; |
|
|
|
import axios from 'axios'; |
|
|
|
import qs from 'qs'; |
|
|
|
import createDropzone from '../features/dropzone.js'; |
|
|
|
import ObsClient from 'esdk-obs-browserjs'; |
|
|
|
|
|
|
|
const {_AppSubUrl, _StaticUrlPrefix, csrf} = window.config; |
|
|
|
const CloudBrainType = 1; |
|
|
|
|
|
|
|
export default { |
|
|
|
data() { |
|
|
@@ -66,7 +68,6 @@ export default { |
|
|
|
|
|
|
|
const $dropzone = $('div#dataset'); |
|
|
|
console.log('createDropzone'); |
|
|
|
|
|
|
|
const dropzoneUploader = await createDropzone($dropzone[0], { |
|
|
|
url: '/todouploader', |
|
|
|
maxFiles: this.maxFiles, |
|
|
@@ -77,16 +78,14 @@ export default { |
|
|
|
dictInvalidFileType: this.dropzoneParams.data('invalid-input-type'), |
|
|
|
dictFileTooBig: this.dropzoneParams.data('file-too-big'), |
|
|
|
dictRemoveFile: this.dropzoneParams.data('remove-file'), |
|
|
|
previewTemplate, |
|
|
|
previewTemplate |
|
|
|
}); |
|
|
|
|
|
|
|
// 将文件加入文件列表 |
|
|
|
dropzoneUploader.on('addedfile', (file) => { |
|
|
|
if(file.status == 'added'){ |
|
|
|
this.onFileAdded(file) |
|
|
|
} |
|
|
|
setTimeout(() => { |
|
|
|
// eslint-disable-next-line no-unused-expressions |
|
|
|
file.accepted && this.onFileAdded(file); |
|
|
|
}, 200); |
|
|
|
}); |
|
|
|
|
|
|
|
dropzoneUploader.on('maxfilesexceeded', function (file) { |
|
|
|
if (this.files[0].status !== 'success') { |
|
|
|
alert(this.dropzoneParams.data('waitting-uploading')); |
|
|
@@ -99,15 +98,14 @@ export default { |
|
|
|
|
|
|
|
this.dropzoneUploader = dropzoneUploader; |
|
|
|
}, |
|
|
|
|
|
|
|
methods: { |
|
|
|
resetStatus() { |
|
|
|
this.progress = 0; |
|
|
|
this.status = ''; |
|
|
|
}, |
|
|
|
updateProgress(file, progress) { |
|
|
|
file.previewTemplate.querySelector( |
|
|
|
'.dz-upload' |
|
|
|
file.previewTemplate.querySelector( |
|
|
|
'.dz-upload' |
|
|
|
).style.width = `${progress}%`; |
|
|
|
}, |
|
|
|
emitDropzoneSuccess(file) { |
|
|
@@ -119,158 +117,326 @@ export default { |
|
|
|
this.status = this.dropzoneParams.data('falied'); |
|
|
|
file.status = 'error'; |
|
|
|
this.dropzoneUploader.emit('error', file); |
|
|
|
// this.dropzoneUploader.emit('complete', file); |
|
|
|
}, |
|
|
|
|
|
|
|
onFileAdded(file) { |
|
|
|
file.datasetId = document |
|
|
|
.getElementById('datasetId') |
|
|
|
.getAttribute('datasetId'); |
|
|
|
this.resetStatus(); |
|
|
|
this.status = this.dropzoneParams.data('obs-connecting'); |
|
|
|
this.do_multi_uploader(file) |
|
|
|
this.computeMD5(file); |
|
|
|
}, |
|
|
|
|
|
|
|
finishUpload(file) { |
|
|
|
this.emitDropzoneSuccess(file); |
|
|
|
setTimeout(() => { |
|
|
|
window.location.reload(); |
|
|
|
}, 1000); |
|
|
|
}, |
|
|
|
|
|
|
|
computeMD5(file) { |
|
|
|
this.resetStatus(); |
|
|
|
const blobSlice = |
|
|
|
File.prototype.slice || |
|
|
|
File.prototype.mozSlice || |
|
|
|
File.prototype.webkitSlice, |
|
|
|
chunkSize = 1024 * 1024 * 64, |
|
|
|
chunks = Math.ceil(file.size / chunkSize), |
|
|
|
spark = new SparkMD5.ArrayBuffer(), |
|
|
|
fileReader = new FileReader(); |
|
|
|
let currentChunk = 0; |
|
|
|
|
|
|
|
const time = new Date().getTime(); |
|
|
|
// console.log('计算MD5...') |
|
|
|
this.status = this.dropzoneParams.data('md5-computing'); |
|
|
|
file.totalChunkCounts = chunks; |
|
|
|
loadNext(); |
|
|
|
|
|
|
|
fileReader.onload = (e) => { |
|
|
|
fileLoaded.call(this, e); |
|
|
|
}; |
|
|
|
fileReader.onerror = (err) => { |
|
|
|
console.warn('oops, something went wrong.', err); |
|
|
|
file.cancel(); |
|
|
|
}; |
|
|
|
|
|
|
|
function fileLoaded(e) { |
|
|
|
spark.append(e.target.result); // Append array buffer |
|
|
|
currentChunk++; |
|
|
|
if (currentChunk < chunks) { |
|
|
|
// console.log(`第${currentChunk}分片解析完成, 开始第${currentChunk +1}/${chunks}分片解析`); |
|
|
|
this.status = `${this.dropzoneParams.data('loading-file')} ${( |
|
|
|
(currentChunk / chunks) * |
|
|
|
100 |
|
|
|
).toFixed(2)}% (${currentChunk}/${chunks})`; |
|
|
|
this.updateProgress(file, ((currentChunk / chunks) * 100).toFixed(2)); |
|
|
|
loadNext(); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const md5 = spark.end(); |
|
|
|
console.log( |
|
|
|
`MD5计算完成:${file.name} \nMD5:${md5} \n分片:${chunks} 大小:${ |
|
|
|
file.size |
|
|
|
} 用时:${(new Date().getTime() - time) / 1000} s` |
|
|
|
); |
|
|
|
spark.destroy(); // 释放缓存 |
|
|
|
file.uniqueIdentifier = md5; // 将文件md5赋值给文件唯一标识 |
|
|
|
file.cmd5 = false; // 取消计算md5状态 |
|
|
|
this.computeMD5Success(file); |
|
|
|
} |
|
|
|
|
|
|
|
function loadNext() { |
|
|
|
const start = currentChunk * chunkSize; |
|
|
|
const end = |
|
|
|
start + chunkSize >= file.size ? file.size : start + chunkSize; |
|
|
|
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end)); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 获取key, uuid |
|
|
|
get_result(){ |
|
|
|
var res |
|
|
|
$.ajax({ |
|
|
|
url: '/attachments/get_obs_key', |
|
|
|
type: 'GET', |
|
|
|
async: false, |
|
|
|
success: function(result){ |
|
|
|
res = result |
|
|
|
async computeMD5Success(md5edFile) { |
|
|
|
const file = await this.getSuccessChunks(md5edFile); |
|
|
|
try { |
|
|
|
if (file.uploadID == '' || file.uuid == '') { |
|
|
|
// 未上传过 |
|
|
|
await this.newMultiUpload(file); |
|
|
|
if (file.uploadID != '' && file.uuid != '') { |
|
|
|
file.chunks = ''; |
|
|
|
this.multipartUpload(file); |
|
|
|
} else { |
|
|
|
// 失败如何处理 |
|
|
|
return; |
|
|
|
} |
|
|
|
return; |
|
|
|
} |
|
|
|
}); |
|
|
|
return res |
|
|
|
}, |
|
|
|
|
|
|
|
// 构建ObsClient |
|
|
|
getObsClient(result){ |
|
|
|
return new ObsClient({ |
|
|
|
access_key_id: result.access_key_id, |
|
|
|
secret_access_key: result.secret_access_key, |
|
|
|
server : result.server |
|
|
|
if (file.uploaded == '1') { |
|
|
|
// 已上传成功 |
|
|
|
// 秒传 |
|
|
|
if (file.attachID == '0') { |
|
|
|
// 删除数据集记录,未删除文件 |
|
|
|
await addAttachment(file); |
|
|
|
} |
|
|
|
//不同数据集上传同一个文件 |
|
|
|
if (file.datasetID != '') { |
|
|
|
if (Number(file.datasetID) != file.datasetId) { |
|
|
|
var info = "该文件已上传,对应数据集(" + file.datasetName + ")-文件(" + file.realName + ")"; |
|
|
|
window.alert(info); |
|
|
|
window.location.reload(); |
|
|
|
} |
|
|
|
} |
|
|
|
console.log('文件已上传完成'); |
|
|
|
this.progress = 100; |
|
|
|
this.status = this.dropzoneParams.data('upload-complete'); |
|
|
|
this.finishUpload(file); |
|
|
|
} else { |
|
|
|
// 断点续传 |
|
|
|
this.multipartUpload(file); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
this.emitDropzoneFailed(file); |
|
|
|
console.log(error); |
|
|
|
} |
|
|
|
|
|
|
|
async function addAttachment(file) { |
|
|
|
return await axios.post( |
|
|
|
'/attachments/add', |
|
|
|
qs.stringify({ |
|
|
|
uuid: file.uuid, |
|
|
|
file_name: file.name, |
|
|
|
size: file.size, |
|
|
|
dataset_id: file.datasetId, |
|
|
|
type: CloudBrainType, |
|
|
|
_csrf: csrf, |
|
|
|
}) |
|
|
|
); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
async getSuccessChunks(file) { |
|
|
|
const params = { |
|
|
|
params: { |
|
|
|
md5: file.uniqueIdentifier, |
|
|
|
type: CloudBrainType, |
|
|
|
_csrf: csrf |
|
|
|
} |
|
|
|
}; |
|
|
|
try { |
|
|
|
const response = await axios.get('/attachments/get_chunks', params); |
|
|
|
file.uploadID = response.data.uploadID; |
|
|
|
file.uuid = response.data.uuid; |
|
|
|
file.uploaded = response.data.uploaded; |
|
|
|
file.chunks = response.data.chunks; |
|
|
|
file.attachID = response.data.attachID; |
|
|
|
file.datasetID = response.data.datasetID; |
|
|
|
file.datasetName = response.data.datasetName; |
|
|
|
file.realName = response.data.fileName; |
|
|
|
return file; |
|
|
|
} catch (error) { |
|
|
|
this.emitDropzoneFailed(file); |
|
|
|
console.log('getSuccessChunks catch: ', error); |
|
|
|
return null; |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
async newMultiUpload(file) { |
|
|
|
const res = await axios.get('/attachments/new_multipart', { |
|
|
|
params: { |
|
|
|
totalChunkCounts: file.totalChunkCounts, |
|
|
|
md5: file.uniqueIdentifier, |
|
|
|
size: file.size, |
|
|
|
fileType: file.type, |
|
|
|
type: CloudBrainType, |
|
|
|
_csrf: csrf |
|
|
|
} |
|
|
|
}); |
|
|
|
file.uploadID = res.data.uploadID; |
|
|
|
file.uuid = res.data.uuid; |
|
|
|
}, |
|
|
|
|
|
|
|
// 断点续传 |
|
|
|
do_multi_uploader(file){ |
|
|
|
|
|
|
|
const result = this.get_result() |
|
|
|
const upload_datasetId = document |
|
|
|
.getElementById('datasetId') |
|
|
|
.getAttribute('datasetId'); |
|
|
|
const obsClient = this.getObsClient(result) |
|
|
|
const _this = this |
|
|
|
var cp; |
|
|
|
var hook; |
|
|
|
multipartUpload(file) { |
|
|
|
const blobSlice = |
|
|
|
File.prototype.slice || |
|
|
|
File.prototype.mozSlice || |
|
|
|
File.prototype.webkitSlice, |
|
|
|
chunkSize = 1024 * 1024 * 64, |
|
|
|
chunks = Math.ceil(file.size / chunkSize), |
|
|
|
fileReader = new FileReader(), |
|
|
|
time = new Date().getTime(); |
|
|
|
let currentChunk = 0; |
|
|
|
|
|
|
|
obsClient.uploadFile({ |
|
|
|
Bucket : result.bucket, |
|
|
|
Key : result.key, |
|
|
|
SourceFile : file, |
|
|
|
PartSize : 64 * 1024 * 1024, |
|
|
|
function loadNext() { |
|
|
|
const start = currentChunk * chunkSize; |
|
|
|
const end = |
|
|
|
start + chunkSize >= file.size ? file.size : start + chunkSize; |
|
|
|
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end)); |
|
|
|
} |
|
|
|
|
|
|
|
// 更新进度条 |
|
|
|
ProgressCallback : function(transferredAmount, totalAmount, totalSeconds){ |
|
|
|
_this.updateProgress(file, ((transferredAmount / totalAmount) * 100).toFixed(2)) |
|
|
|
_this.status = `${_this.dropzoneParams.data('uploading')} ${( |
|
|
|
(transferredAmount / totalAmount) * |
|
|
|
100 |
|
|
|
).toFixed(2)}%`; |
|
|
|
}, |
|
|
|
function checkSuccessChunks() { |
|
|
|
const index = successChunks.indexOf((currentChunk + 1).toString()); |
|
|
|
if (index == -1) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
// 监听文件上传结果 |
|
|
|
EventCallback : function(eventType, eventParam, eventResult){ |
|
|
|
console.log("eventType1= ", eventType) |
|
|
|
console.log("eventParam1= ", eventParam) |
|
|
|
console.log("eventResult1= ", eventResult) |
|
|
|
// 文件上传成功 |
|
|
|
if(eventType == 'completeMultipartUploadSucceed'){ |
|
|
|
console.log("file = ", file) |
|
|
|
$.ajax({ |
|
|
|
url: '/attachments/add', |
|
|
|
type: 'POST', |
|
|
|
data: { |
|
|
|
'uuid': result.uuid, |
|
|
|
'file_name': file.name, |
|
|
|
'size': file.size, |
|
|
|
'dataset_id': upload_datasetId, |
|
|
|
'_csrf': csrf, |
|
|
|
'type': 1 |
|
|
|
}, |
|
|
|
async: false, |
|
|
|
success: function (data) { |
|
|
|
_this.progress = 100; |
|
|
|
_this.status = _this.dropzoneParams.data('upload-complete'); |
|
|
|
_this.emitDropzoneSuccess(file) |
|
|
|
setTimeout(() => { |
|
|
|
window.location.reload(); |
|
|
|
}, 1000); |
|
|
|
}, |
|
|
|
error: function(){ |
|
|
|
_this.emitDropzoneFailed(file) |
|
|
|
} |
|
|
|
}); |
|
|
|
async function getUploadChunkUrl(currentChunk, partSize) { |
|
|
|
const res = await axios.get('/attachments/get_multipart_url', { |
|
|
|
params: { |
|
|
|
uuid: file.uuid, |
|
|
|
uploadID: file.uploadID, |
|
|
|
size: partSize, |
|
|
|
chunkNumber: currentChunk + 1, |
|
|
|
type: CloudBrainType, |
|
|
|
_csrf: csrf |
|
|
|
} |
|
|
|
}, |
|
|
|
ResumeCallback : function(resumeHook, uploadCheckpoint){ |
|
|
|
hook = resumeHook; |
|
|
|
cp = uploadCheckpoint; |
|
|
|
} |
|
|
|
}, function(err, result){ |
|
|
|
// 出现错误,再次调用断点续传接口,继续上传任务 |
|
|
|
if(err){ |
|
|
|
obsClient.uploadFile({ |
|
|
|
UploadCheckpoint : cp, |
|
|
|
}); |
|
|
|
urls[currentChunk] = res.data.url; |
|
|
|
} |
|
|
|
|
|
|
|
// 断点续传后继续更新进度条 |
|
|
|
ProgressCallback : function(transferredAmount, totalAmount, totalSeconds){ |
|
|
|
_this.updateProgress(file, ((transferredAmount / totalAmount) * 100).toFixed(2)) |
|
|
|
_this.status = `${_this.dropzoneParams.data('uploading')} ${( |
|
|
|
(transferredAmount / totalAmount) * |
|
|
|
100 |
|
|
|
).toFixed(2)}%`; |
|
|
|
}, |
|
|
|
async function uploadMinio(url, e) { |
|
|
|
let urls = []; |
|
|
|
const res = await axios.put(url, e.target.result, { |
|
|
|
headers: { |
|
|
|
'Content-Type': '' |
|
|
|
}}); |
|
|
|
etags[currentChunk] = res.headers.etag; |
|
|
|
} |
|
|
|
|
|
|
|
// 监听断点续传的结果 |
|
|
|
EventCallback : function(eventType, eventParam, eventResult){ |
|
|
|
console.log("eventType2= ", eventType) |
|
|
|
console.log("eventParam2= ", eventParam) |
|
|
|
console.log("eventResult2= ", eventResult) |
|
|
|
// 文件断点续传成功 |
|
|
|
if(eventType == 'completeMultipartUploadSucceed'){ |
|
|
|
$.ajax({ |
|
|
|
url: '/attachments/add', |
|
|
|
type: 'POST', |
|
|
|
data: { |
|
|
|
'uuid': result.uuid, |
|
|
|
'file_name': file.name, |
|
|
|
'size': file.size, |
|
|
|
'dataset_id': upload_datasetId, |
|
|
|
'_csrf': csrf, |
|
|
|
'type': 1 |
|
|
|
}, |
|
|
|
async: false, |
|
|
|
success: function (data) { |
|
|
|
_this.progress = 100; |
|
|
|
_this.status = _this.dropzoneParams.data('upload-complete'); |
|
|
|
_this.emitDropzoneSuccess(file) |
|
|
|
setTimeout(() => { |
|
|
|
window.location.reload(); |
|
|
|
}, 1000); |
|
|
|
console.log(data) |
|
|
|
}, |
|
|
|
error: function(){ |
|
|
|
_this.emitDropzoneFailed(file) |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
if (eventType == 'uploadPartFailed'){ |
|
|
|
_this.emitDropzoneFailed(file) |
|
|
|
} |
|
|
|
async function updateChunk(currentChunk) { |
|
|
|
await axios.post( |
|
|
|
'/attachments/update_chunk', |
|
|
|
qs.stringify({ |
|
|
|
uuid: file.uuid, |
|
|
|
chunkNumber: currentChunk + 1, |
|
|
|
etag: etags[currentChunk], |
|
|
|
type: CloudBrainType, |
|
|
|
_csrf: csrf |
|
|
|
}) |
|
|
|
); |
|
|
|
} |
|
|
|
async function uploadChunk(e) { |
|
|
|
try { |
|
|
|
if (!checkSuccessChunks()) { |
|
|
|
const start = currentChunk * chunkSize; |
|
|
|
const partSize = |
|
|
|
start + chunkSize >= file.size ? file.size - start : chunkSize; |
|
|
|
// 获取分片上传url |
|
|
|
await getUploadChunkUrl(currentChunk, partSize); |
|
|
|
if (urls[currentChunk] != '') { |
|
|
|
// 上传到minio |
|
|
|
await uploadMinio(urls[currentChunk], e); |
|
|
|
if (etags[currentChunk] != '') { |
|
|
|
// 更新数据库:分片上传结果 |
|
|
|
//await updateChunk(currentChunk); |
|
|
|
} else { |
|
|
|
console.log("上传到minio uploadChunk etags[currentChunk] == ''");// TODO |
|
|
|
} |
|
|
|
}); |
|
|
|
} else { |
|
|
|
console.log("uploadChunk urls[currentChunk] != ''");// TODO |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}, |
|
|
|
} catch (error) { |
|
|
|
this.emitDropzoneFailed(file); |
|
|
|
console.log(error); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
async function completeUpload() { |
|
|
|
return await axios.post( |
|
|
|
'/attachments/complete_multipart', |
|
|
|
qs.stringify({ |
|
|
|
uuid: file.uuid, |
|
|
|
uploadID: file.uploadID, |
|
|
|
file_name: file.name, |
|
|
|
size: file.size, |
|
|
|
dataset_id: file.datasetId, |
|
|
|
type: CloudBrainType, |
|
|
|
_csrf: csrf |
|
|
|
}) |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
const successChunks = []; |
|
|
|
let successParts = []; |
|
|
|
successParts = file.chunks.split(','); |
|
|
|
for (let i = 0; i < successParts.length; i++) { |
|
|
|
successChunks[i] = successParts[i].split('-')[0]; |
|
|
|
} |
|
|
|
const urls = []; // TODO const ? |
|
|
|
const etags = []; |
|
|
|
console.log('上传分片...'); |
|
|
|
this.status = this.dropzoneParams.data('uploading'); |
|
|
|
loadNext(); |
|
|
|
fileReader.onload = async (e) => { |
|
|
|
await uploadChunk(e); |
|
|
|
fileReader.abort(); |
|
|
|
currentChunk++; |
|
|
|
if (currentChunk < chunks) { |
|
|
|
console.log( |
|
|
|
`第${currentChunk}个分片上传完成, 开始第${currentChunk + |
|
|
|
1}/${chunks}个分片上传` |
|
|
|
); |
|
|
|
this.progress = Math.ceil((currentChunk / chunks) * 100); |
|
|
|
this.updateProgress(file, ((currentChunk / chunks) * 100).toFixed(2)); |
|
|
|
this.status = `${this.dropzoneParams.data('uploading')} ${( |
|
|
|
(currentChunk / chunks) * |
|
|
|
100 |
|
|
|
).toFixed(2)}%`; |
|
|
|
await loadNext(); |
|
|
|
} else { |
|
|
|
await completeUpload(); |
|
|
|
console.log( |
|
|
|
`文件上传完成:${file.name} \n分片:${chunks} 大小:${ |
|
|
|
file.size |
|
|
|
} 用时:${(new Date().getTime() - time) / 1000} s` |
|
|
|
); |
|
|
|
this.progress = 100; |
|
|
|
this.status = this.dropzoneParams.data('upload-complete'); |
|
|
|
this.finishUpload(file); |
|
|
|
} |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
</script> |
|
|
|