Browse Source

change vue-uploader -> dropzone

tags/v1.21.12.1
palytoxin 5 years ago
parent
commit
46ef33edb8
5 changed files with 354 additions and 378 deletions
  1. +17
    -9
      package-lock.json
  2. +1
    -1
      package.json
  3. +0
    -362
      web_src/js/App.vue
  4. +331
    -0
      web_src/js/components/MinioUploader.vue
  5. +5
    -6
      web_src/js/index.js

+ 17
- 9
package-lock.json View File

@@ -1797,7 +1797,8 @@
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"optional": true
},
"assign-symbols": {
"version": "1.0.0",
@@ -2854,6 +2855,7 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"optional": true,
"requires": {
"delayed-stream": "~1.0.0"
}
@@ -3590,7 +3592,8 @@
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"optional": true
},
"delegate": {
"version": "3.2.0",
@@ -3744,9 +3747,9 @@
}
},
"dropzone": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/dropzone/-/dropzone-5.7.0.tgz",
"integrity": "sha512-kOltiZXH5cO/72I22JjE+w6BoT6uaVLfWdFMsi1PMKFkU6BZWpqRwjnsRm0o6ANGTBuZar5Piu7m/CbKqRPiYg=="
"version": "5.7.2",
"resolved": "https://registry.npm.taobao.org/dropzone/download/dropzone-5.7.2.tgz?cache=0&sync_timestamp=1596009792692&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdropzone%2Fdownload%2Fdropzone-5.7.2.tgz",
"integrity": "sha1-kb7hVy3aUV1AkB2jBLx53d8wm0w="
},
"duplexer2": {
"version": "0.0.2",
@@ -4806,7 +4809,8 @@
"extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
"optional": true
},
"fancy-log": {
"version": "1.3.3",
@@ -7621,7 +7625,8 @@
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
"optional": true
},
"jsesc": {
"version": "2.5.2",
@@ -8677,12 +8682,14 @@
"mime-db": {
"version": "1.44.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==",
"optional": true
},
"mime-types": {
"version": "2.1.27",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
"optional": true,
"requires": {
"mime-db": "1.44.0"
}
@@ -13654,7 +13661,8 @@
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"optional": true
},
"type": {
"version": "1.2.0",


+ 1
- 1
package.json View File

@@ -18,7 +18,7 @@
"css-loader": "3.5.3",
"cssnano": "4.1.10",
"domino": "2.1.5",
"dropzone": "5.7.0",
"dropzone": "5.7.2",
"fast-glob": "3.2.2",
"file-loader": "6.0.0",
"fomantic-ui": "2.8.4",


+ 0
- 362
web_src/js/App.vue View File

@@ -1,362 +0,0 @@
<template>
<uploader
ref="uploader"
:options="options"
:autoStart="false"
@file-added="onFileAdded"
fileStatusText="fileStatusText"
class="uploader-app">
<uploader-unsupport></uploader-unsupport>
<uploader-drop>
<p>拖动文件</p>
<uploader-btn>选择文件</uploader-btn>
</uploader-drop>
<uploader-list></uploader-list>
<p>文件处理状态:{{status}}</p>
<p>文件上传进度:{{progress}}%</p>
</uploader>
</template>

<script>

import SparkMD5 from 'spark-md5';
import axios from 'axios'
import qs from 'qs'

const {AppSubUrl, StaticUrlPrefix, csrf} = window.config;

export default {
data () {
return {
attrs: {
accept: '*'
},
fileStatusText: {
success: '上传成功',
error: '上传出错了',
uploading: '上传中...',
paused: '暂停',
waiting: '等待中...',
cmd5: '计算md5...'
},
fileStatusText: (status, response) => {
return this.fileStatusText[status];
},
progress: 0,
status: '初始状态',
}
},
created() {
//const uploaderInstance = this.$refs.uploader;
},
mounted () {
this.$nextTick(() => {
window.uploader = this.$refs.uploader.uploader
})
},
methods: {
onFileAdded(file) {
file.datasetId = document.getElementById("datasetId").getAttribute("datasetId");
this.progress=0;
this.status='初始状态';
// 计算MD5
this.computeMD5(file);
},
getSuccessChunks(file) {
return new Promise((resolve, reject) => {
axios.get('/attachments/get_chunks', {params :{
md5: file.uniqueIdentifier,
_csrf: csrf
}}).then(function (response) {
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;
resolve(response);
}).catch(function (error) {
console.log(error);
reject(error);
});
})

},
newMultiUpload(file) {
return new Promise((resolve, reject) => {
axios.get('/attachments/new_multipart', {params :{
totalChunkCounts: file.totalChunkCounts,
md5: file.uniqueIdentifier,
size: file.size,
fileType: file.fileType,
_csrf: csrf
}}).then(function (response) {
file.uploadID = response.data.uploadID;
file.uuid = response.data.uuid;
resolve(response);
}).catch(function (error) {
console.log(error);
reject(error);
});
})
},
multipartUpload(file) {
let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
chunkSize = 1024*1024*64,
chunks = Math.ceil(file.size / chunkSize),
currentChunk = 0,
fileReader = new FileReader(),
time = new Date().getTime();

function loadNext() {
let start = currentChunk * chunkSize;
let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;

fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
}

function checkSuccessChunks() {
var index = successChunks.indexOf((currentChunk+1).toString())
if (index == -1) {
return false;
}

return true;
}

function getUploadChunkUrl(currentChunk, partSize) {
return new Promise((resolve, reject) => {
axios.get('/attachments/get_multipart_url', {params :{
uuid: file.uuid,
uploadID: file.uploadID,
size: partSize,
chunkNumber: currentChunk+1,
_csrf: csrf
}}).then(function (response) {
urls[currentChunk] = response.data.url
resolve(response);
}).catch(function (error) {
console.log(error);
reject(error);
});
})
}

function uploadMinio(url, e) {
return new Promise((resolve, reject) => {
axios.put(url, e.target.result
).then(function (res) {
etags[currentChunk] = res.headers.etag;
resolve(res);
}).catch(function (err) {
console.log(err);
reject(err);
});
});
}

function updateChunk(currentChunk) {
return new Promise((resolve, reject) => {
axios.post('/attachments/update_chunk', qs.stringify({
uuid: file.uuid,
chunkNumber: currentChunk+1,
etag: etags[currentChunk],
_csrf: csrf
})).then(function (response) {
resolve(response);
}).catch(function (error) {
console.log(error);
reject(error);
});
})
}

async function uploadChunk(e) {
if (!checkSuccessChunks()) {
let start = currentChunk * chunkSize;
let 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 {
return;
}
} else {
return;
}
}
};

function completeUpload(){
return new Promise((resolve, reject) => {
axios.post('/attachments/complete_multipart', qs.stringify({
uuid: file.uuid,
uploadID: file.uploadID,
file_name: file.name,
size: file.size,
dataset_id: file.datasetId,
_csrf: csrf
})).then(function (response) {
resolve(response);
}).catch(function (error) {
console.log(error);
reject(error);
});
})
}

var successChunks = new Array();
var successParts = new Array();
successParts = file.chunks.split(",");
for (let i = 0; i < successParts.length; i++) {
successChunks[i] = successParts[i].split("-")[0].split("\"")[1];
}
var urls = new Array();
var etags = new Array();

console.log('上传分片...');
this.status='上传中';
{
loadNext();
fileReader.onload = async (e) => {
await uploadChunk(e);
currentChunk++;
if (currentChunk < chunks) {
console.log(`第${currentChunk}个分片上传完成, 开始第${currentChunk +1}/${chunks}个分片上传`);
this.progress = Math.ceil((currentChunk / chunks)*100);
await loadNext();
} else {
await completeUpload();
console.log(`文件上传完成:${file.name} \n分片:${chunks} 大小:${file.size} 用时:${(new Date().getTime() - time)/1000} s`);
this.progress = 100;
this.status='上传完成';
window.location.reload();
}
};
}

},
//计算MD5
computeMD5(file) {
let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
chunkSize = 1024*1024*64,
chunks = Math.ceil(file.size / chunkSize),
currentChunk = 0,
spark = new SparkMD5.ArrayBuffer(),
fileReader = new FileReader();

let time = new Date().getTime();

console.log('计算MD5...')
this.status='计算MD5';
file.totalChunkCounts = chunks;
loadNext();

fileReader.onload = (e) => {
spark.append(e.target.result); // Append array buffer
currentChunk++;
if (currentChunk < chunks) {
console.log(`第${currentChunk}分片解析完成, 开始第${currentChunk +1}/${chunks}分片解析`);
loadNext();
} else {
let 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);
}
};

fileReader.onerror = () => {
console.warn('oops, something went wrong.');
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));
}
},
async computeMD5Success(file) {
await this.getSuccessChunks(file);
if (file.uploadID == "" || file.uuid == "") { //未上传过
await this.newMultiUpload(file);
if (file.uploadID != "" && file.uuid != "") {
file.chunks = "";
this.multipartUpload(file);
} else {
//失败如何处理
return;
}
} else {
if (file.uploaded == "1") { //已上传成功
//秒传
if (file.attachID == "0") { //删除数据集记录,未删除文件
await addAttachment(file);
}
console.log("文件已上传完成");
this.progress = 100;
this.status='上传完成';
window.location.reload();
} else {
//断点续传
this.multipartUpload(file);
}
}

function addAttachment(file){
return new Promise((resolve, reject) => {
axios.post('/attachments/add', qs.stringify({
uuid: file.uuid,
file_name: file.name,
size: file.size,
dataset_id: file.datasetId,
_csrf: csrf
})).then(function (response) {
resolve(response);
}).catch(function (error) {
console.log(error);
reject(error);
});
})
}
}
}
}
</script>

<style>
.uploader-app {
width: 850px;
padding: 15px;
margin: 40px auto 0;
font-size: 12px;
box-shadow: 0 0 10px rgba(0, 0, 0, .4);
}
.uploader-app .uploader-btn {
margin-right: 40px;
}
.uploader-app .uploader-list {
max-height: 440px;
overflow: auto;
overflow-x: hidden;
overflow-y: auto;
}
</style>

+ 331
- 0
web_src/js/components/MinioUploader.vue View File

@@ -0,0 +1,331 @@
<template>
<div class="dropzone-wrapper">
<div id="dropzone" class="dropzone">
</div>
<p>文件处理状态:{{ status }}</p>
<p>文件上传进度:{{ progress }}%</p>
</div>
</template>

<script>
import Dropzone from 'dropzone/dist/dropzone.js';
// import 'dropzone/dist/dropzone.css'
import createDropzone from '../features/dropzone.js';
import SparkMD5 from 'spark-md5';
import axios from 'axios';
import qs from 'qs';

const {
AppSubUrl,
StaticUrlPrefix,
csrf
} = window.config;

export default {
data() {
return {
dropzoneUploader: null,
maxFiles: 1,
maxFilesize: 1 * 1024 * 1024 * 1024 * 1024,
acceptedFiles: '*/*',
progress: 0,
status: '等待上传',
}
},

async mounted() {
const dropzoneUploader = await createDropzone("div#dropzone", {
url: '/todouploader',
maxFiles: this.maxFiles,
maxFilesize: this.maxFileSize,
timeout: 0,
autoQueue: false,
})
dropzoneUploader.on("addedfile", (file) => {
setTimeout(() => {
file.accepted && this.onFileAdded(file);
}, 200);
});
dropzoneUploader.on("maxfilesexceeded", function(file) {
if (this.files[0].status !== 'success') {
alert('请等待第一个文件传输完成')
this.removeFile(file)
return
}
this.removeAllFiles();
this.addFile(file);
});

this.dropzoneUploader = dropzoneUploader

},
methods: {
resetStatus() {
this.progress = 0
this.status = ''
},
updateProgress(file, progress) {
file.previewTemplate.querySelector(".dz-upload").style.width = `${progress}%`;
},
emitDropzoneSuccess(file) {
file.status = Dropzone.SUCCESS;
this.dropzoneUploader.emit("success", file);
this.dropzoneUploader.emit("complete", file);
},
onFileAdded(file) {
file.datasetId = document.getElementById("datasetId").getAttribute("datasetId");
this.resetStatus()
this.computeMD5(file);
},

finishUpload(file) {
this.emitDropzoneSuccess(file)
setTimeout(() => {
window.location.reload();
}, 1000);
},

computeMD5(file) {
this.resetStatus()
let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
chunkSize = 1024 * 1024 * 64,
chunks = Math.ceil(file.size / chunkSize),
currentChunk = 0,
spark = new SparkMD5.ArrayBuffer(),
fileReader = new FileReader();
let time = new Date().getTime();
// console.log('计算MD5...')
this.status = '计算MD5';
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 = `加载文件 ${(currentChunk/chunks*100).toFixed(2)}% (${currentChunk}/${chunks})`;
this.updateProgress(file, (currentChunk/chunks*100).toFixed(2))
loadNext();
return
}

let 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() {
let start = currentChunk * chunkSize;
let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
}
},

async computeMD5Success(md5edFile) {
const file = await this.getSuccessChunks(md5edFile);
if (file.uploadID == "" || file.uuid == "") { //未上传过
await this.newMultiUpload(file);
if (file.uploadID != "" && file.uuid != "") {
file.chunks = "";
this.multipartUpload(file);
} else {
//失败如何处理
return;
}
return
}

if (file.uploaded == "1") { //已上传成功
//秒传
if (file.attachID == "0") { //删除数据集记录,未删除文件
await addAttachment(file);
}
console.log("文件已上传完成");
this.progress = 100;
this.status = '上传完成';
this.finishUpload(file)
} else {
//断点续传
this.multipartUpload(file);
}


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,
_csrf: csrf
}))
}
},

async getSuccessChunks(file) {
const params = {
params: {
md5: file.uniqueIdentifier,
_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;
return file
} catch(error) {
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.fileType,
_csrf: csrf
}
})
file.uploadID = res.data.uploadID;
file.uuid = res.data.uuid;
},

multipartUpload(file) {
let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
chunkSize = 1024 * 1024 * 64,
chunks = Math.ceil(file.size / chunkSize),
currentChunk = 0,
fileReader = new FileReader(),
time = new Date().getTime();

function loadNext() {
let start = currentChunk * chunkSize;
let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
}

function checkSuccessChunks() {
var index = successChunks.indexOf((currentChunk + 1).toString())
if (index == -1) {
return false;
}
return true;
}

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,
_csrf: csrf
}
})
console.log("getUploadChunkUrl: ", res)
urls[currentChunk] = res.data.url
}

async function uploadMinio(url, e) {
const res = await axios.put(url, e.target.result)
etags[currentChunk] = res.headers.etag;
}

async function updateChunk(currentChunk) {
await axios.post('/attachments/update_chunk', qs.stringify({
uuid: file.uuid,
chunkNumber: currentChunk + 1,
etag: etags[currentChunk],
_csrf: csrf
}))
}
async function uploadChunk(e) {
if (!checkSuccessChunks()) {
let start = currentChunk * chunkSize;
let 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 {
return;
}
} else {
return;
}
}
};

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,
_csrf: csrf
}))
}

var successChunks = new Array();
var successParts = new Array();
successParts = file.chunks.split(",");
for (let i = 0; i < successParts.length; i++) {
successChunks[i] = successParts[i].split("-")[0].split("\"")[1];
}
var urls = new Array();
var etags = new Array();
console.log('上传分片...');
this.status = '上传中'
loadNext();
fileReader.onload = async (e) => {
await uploadChunk(e);
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))
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.finishUpload(file)
}
};
},

}
}
</script>

<style>
.dropzone-wrapper {
margin: 2em auto ;
}
</style>

+ 5
- 6
web_src/js/index.js View File

@@ -8,6 +8,7 @@ import './polyfills.js';
import Vue from 'vue';
import 'jquery.are-you-sure';
import './vendor/semanticdropdown.js';
import VueSimpleUploader from 'vue-simple-uploader';
import {svg} from './utils.js';

import initContextPopups from './features/contextpopup.js';
@@ -21,11 +22,9 @@ import highlight from './features/highlight.js';
import ActivityTopAuthors from './components/ActivityTopAuthors.vue';
import {initNotificationsTable, initNotificationCount} from './features/notification.js';
import {createCodeEditor} from './features/codeeditor.js';
import App from './App.vue'
import uploader from 'vue-simple-uploader'

Vue.use(uploader);
import MinioUploader from './components/MinioUploader.vue';

Vue.use(VueSimpleUploader);

const {AppSubUrl, StaticUrlPrefix, csrf} = window.config;

@@ -3175,8 +3174,8 @@ function initVueUploader() {
/* eslint-disable no-new */
new Vue({
el: '#uploader',
components: { App },
template: '<App/>'
components: {MinioUploader},
template: '<MinioUploader />'
});
}



Loading…
Cancel
Save