You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

App.vue 12 kB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. <template>
  2. <uploader
  3. ref="uploader"
  4. :options="options"
  5. :autoStart="false"
  6. @file-added="onFileAdded"
  7. fileStatusText="fileStatusText"
  8. class="uploader-app">
  9. <uploader-unsupport></uploader-unsupport>
  10. <uploader-drop>
  11. <p>拖动文件</p>
  12. <uploader-btn>选择文件</uploader-btn>
  13. </uploader-drop>
  14. <uploader-list></uploader-list>
  15. <p>文件处理状态:{{status}}</p>
  16. <p>文件上传进度:{{progress}}%</p>
  17. </uploader>
  18. </template>
  19. <script>
  20. import SparkMD5 from 'spark-md5';
  21. import axios from 'axios'
  22. import qs from 'qs'
  23. const {AppSubUrl, StaticUrlPrefix, csrf} = window.config;
  24. export default {
  25. data () {
  26. return {
  27. attrs: {
  28. accept: '*'
  29. },
  30. fileStatusText: {
  31. success: '上传成功',
  32. error: '上传出错了',
  33. uploading: '上传中...',
  34. paused: '暂停',
  35. waiting: '等待中...',
  36. cmd5: '计算md5...'
  37. },
  38. fileStatusText: (status, response) => {
  39. return this.fileStatusText[status];
  40. },
  41. progress: 0,
  42. status: '初始状态',
  43. }
  44. },
  45. created() {
  46. //const uploaderInstance = this.$refs.uploader;
  47. },
  48. mounted () {
  49. this.$nextTick(() => {
  50. window.uploader = this.$refs.uploader.uploader
  51. })
  52. },
  53. methods: {
  54. onFileAdded(file) {
  55. file.datasetId = document.getElementById("datasetId").getAttribute("datasetId");
  56. this.progress=0;
  57. this.status='初始状态';
  58. // 计算MD5
  59. this.computeMD5(file);
  60. },
  61. getSuccessChunks(file) {
  62. return new Promise((resolve, reject) => {
  63. axios.get('/attachments/get_chunks', {params :{
  64. md5: file.uniqueIdentifier,
  65. _csrf: csrf
  66. }}).then(function (response) {
  67. file.uploadID = response.data.uploadID;
  68. file.uuid = response.data.uuid;
  69. file.uploaded = response.data.uploaded;
  70. file.chunks = response.data.chunks;
  71. file.attachID = response.data.attachID;
  72. resolve(response);
  73. }).catch(function (error) {
  74. console.log(error);
  75. reject(error);
  76. });
  77. })
  78. },
  79. newMultiUpload(file) {
  80. return new Promise((resolve, reject) => {
  81. axios.get('/attachments/new_multipart', {params :{
  82. totalChunkCounts: file.totalChunkCounts,
  83. md5: file.uniqueIdentifier,
  84. size: file.size,
  85. fileType: file.fileType,
  86. _csrf: csrf
  87. }}).then(function (response) {
  88. file.uploadID = response.data.uploadID;
  89. file.uuid = response.data.uuid;
  90. resolve(response);
  91. }).catch(function (error) {
  92. console.log(error);
  93. reject(error);
  94. });
  95. })
  96. },
  97. multipartUpload(file) {
  98. let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
  99. chunkSize = 1024*1024*64,
  100. chunks = Math.ceil(file.size / chunkSize),
  101. currentChunk = 0,
  102. fileReader = new FileReader(),
  103. time = new Date().getTime();
  104. function loadNext() {
  105. let start = currentChunk * chunkSize;
  106. let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
  107. fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
  108. }
  109. function checkSuccessChunks() {
  110. var index = successChunks.indexOf((currentChunk+1).toString())
  111. if (index == -1) {
  112. return false;
  113. }
  114. return true;
  115. }
  116. function getUploadChunkUrl(currentChunk, partSize) {
  117. return new Promise((resolve, reject) => {
  118. axios.get('/attachments/get_multipart_url', {params :{
  119. uuid: file.uuid,
  120. uploadID: file.uploadID,
  121. size: partSize,
  122. chunkNumber: currentChunk+1,
  123. _csrf: csrf
  124. }}).then(function (response) {
  125. urls[currentChunk] = response.data.url
  126. resolve(response);
  127. }).catch(function (error) {
  128. console.log(error);
  129. reject(error);
  130. });
  131. })
  132. }
  133. function uploadMinio(url, e) {
  134. return new Promise((resolve, reject) => {
  135. axios.put(url, e.target.result
  136. ).then(function (res) {
  137. etags[currentChunk] = res.headers.etag;
  138. resolve(res);
  139. }).catch(function (err) {
  140. console.log(err);
  141. reject(err);
  142. });
  143. });
  144. }
  145. function updateChunk(currentChunk) {
  146. return new Promise((resolve, reject) => {
  147. axios.post('/attachments/update_chunk', qs.stringify({
  148. uuid: file.uuid,
  149. chunkNumber: currentChunk+1,
  150. etag: etags[currentChunk],
  151. _csrf: csrf
  152. })).then(function (response) {
  153. resolve(response);
  154. }).catch(function (error) {
  155. console.log(error);
  156. reject(error);
  157. });
  158. })
  159. }
  160. async function uploadChunk(e) {
  161. if (!checkSuccessChunks()) {
  162. let start = currentChunk * chunkSize;
  163. let partSize = ((start + chunkSize) >= file.size) ? file.size -start : chunkSize;
  164. //获取分片上传url
  165. await getUploadChunkUrl(currentChunk, partSize);
  166. if (urls[currentChunk] != "") {
  167. //上传到minio
  168. await uploadMinio(urls[currentChunk], e);
  169. if (etags[currentChunk] != "") {
  170. //更新数据库:分片上传结果
  171. await updateChunk(currentChunk);
  172. } else {
  173. return;
  174. }
  175. } else {
  176. return;
  177. }
  178. }
  179. };
  180. function completeUpload(){
  181. return new Promise((resolve, reject) => {
  182. axios.post('/attachments/complete_multipart', qs.stringify({
  183. uuid: file.uuid,
  184. uploadID: file.uploadID,
  185. file_name: file.name,
  186. size: file.size,
  187. dataset_id: file.datasetId,
  188. _csrf: csrf
  189. })).then(function (response) {
  190. resolve(response);
  191. }).catch(function (error) {
  192. console.log(error);
  193. reject(error);
  194. });
  195. })
  196. }
  197. var successChunks = new Array();
  198. var successParts = new Array();
  199. successParts = file.chunks.split(",");
  200. for (let i = 0; i < successParts.length; i++) {
  201. successChunks[i] = successParts[i].split("-")[0].split("\"")[1];
  202. }
  203. var urls = new Array();
  204. var etags = new Array();
  205. console.log('上传分片...');
  206. this.status='上传中';
  207. {
  208. loadNext();
  209. fileReader.onload = async (e) => {
  210. await uploadChunk(e);
  211. currentChunk++;
  212. if (currentChunk < chunks) {
  213. console.log(`第${currentChunk}个分片上传完成, 开始第${currentChunk +1}/${chunks}个分片上传`);
  214. this.progress = Math.ceil((currentChunk / chunks)*100);
  215. await loadNext();
  216. } else {
  217. await completeUpload();
  218. console.log(`文件上传完成:${file.name} \n分片:${chunks} 大小:${file.size} 用时:${(new Date().getTime() - time)/1000} s`);
  219. this.progress = 100;
  220. this.status='上传完成';
  221. window.location.reload();
  222. }
  223. };
  224. }
  225. },
  226. //计算MD5
  227. computeMD5(file) {
  228. let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
  229. chunkSize = 1024*1024*64,
  230. chunks = Math.ceil(file.size / chunkSize),
  231. currentChunk = 0,
  232. spark = new SparkMD5.ArrayBuffer(),
  233. fileReader = new FileReader();
  234. let time = new Date().getTime();
  235. console.log('计算MD5...')
  236. this.status='计算MD5';
  237. file.totalChunkCounts = chunks;
  238. loadNext();
  239. fileReader.onload = (e) => {
  240. spark.append(e.target.result); // Append array buffer
  241. currentChunk++;
  242. if (currentChunk < chunks) {
  243. console.log(`第${currentChunk}分片解析完成, 开始第${currentChunk +1}/${chunks}分片解析`);
  244. loadNext();
  245. } else {
  246. let md5 = spark.end();
  247. console.log(`MD5计算完成:${file.name} \nMD5:${md5} \n分片:${chunks} 大小:${file.size} 用时:${(new Date().getTime() - time)/1000} s`);
  248. spark.destroy(); //释放缓存
  249. file.uniqueIdentifier = md5; //将文件md5赋值给文件唯一标识
  250. file.cmd5 = false; //取消计算md5状态
  251. this.computeMD5Success(file);
  252. }
  253. };
  254. fileReader.onerror = () => {
  255. console.warn('oops, something went wrong.');
  256. file.cancel();
  257. };
  258. function loadNext() {
  259. let start = currentChunk * chunkSize;
  260. let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
  261. fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
  262. }
  263. },
  264. async computeMD5Success(file) {
  265. await this.getSuccessChunks(file);
  266. if (file.uploadID == "" || file.uuid == "") { //未上传过
  267. await this.newMultiUpload(file);
  268. if (file.uploadID != "" && file.uuid != "") {
  269. file.chunks = "";
  270. this.multipartUpload(file);
  271. } else {
  272. //失败如何处理
  273. return;
  274. }
  275. } else {
  276. if (file.uploaded == "1") { //已上传成功
  277. //秒传
  278. if (file.attachID == "0") { //删除数据集记录,未删除文件
  279. await addAttachment(file);
  280. }
  281. console.log("文件已上传完成");
  282. this.progress = 100;
  283. this.status='上传完成';
  284. window.location.reload();
  285. } else {
  286. //断点续传
  287. this.multipartUpload(file);
  288. }
  289. }
  290. function addAttachment(file){
  291. return new Promise((resolve, reject) => {
  292. axios.post('/attachments/add', qs.stringify({
  293. uuid: file.uuid,
  294. file_name: file.name,
  295. size: file.size,
  296. dataset_id: file.datasetId,
  297. _csrf: csrf
  298. })).then(function (response) {
  299. resolve(response);
  300. }).catch(function (error) {
  301. console.log(error);
  302. reject(error);
  303. });
  304. })
  305. }
  306. }
  307. }
  308. }
  309. </script>
  310. <style>
  311. .uploader-app {
  312. width: 850px;
  313. padding: 15px;
  314. margin: 40px auto 0;
  315. font-size: 12px;
  316. box-shadow: 0 0 10px rgba(0, 0, 0, .4);
  317. }
  318. .uploader-app .uploader-btn {
  319. margin-right: 40px;
  320. }
  321. .uploader-app .uploader-list {
  322. max-height: 440px;
  323. overflow: auto;
  324. overflow-x: hidden;
  325. overflow-y: auto;
  326. }
  327. </style>