| @@ -993,7 +993,7 @@ func (c Client) newRequest(method string, metadata requestMetadata) (req *http.R | |||
| } | |||
| func (c Client) GenUploadPartSignedUrl(uploadID string, bucketName string, objectName string, partNumber int, size int64, expires int64) (string, error){ | |||
| func (c Client) GenUploadPartSignedUrl(uploadID string, bucketName string, objectName string, partNumber int, size int64, expires time.Duration) (string, error){ | |||
| signedUrl := "" | |||
| // Input validation. | |||
| @@ -1036,7 +1036,7 @@ func (c Client) GenUploadPartSignedUrl(uploadID string, bucketName string, objec | |||
| contentLength: size, | |||
| //contentMD5Base64: md5Base64, | |||
| //contentSHA256Hex: sha256Hex, | |||
| expires: expires, | |||
| expires: int64(expires/time.Second), | |||
| } | |||
| req, err := c.newRequest("PUT", reqMetadata) | |||
| @@ -17,7 +17,7 @@ import ( | |||
| ) | |||
| const ( | |||
| PresignedUploadPartUrlExpireTime = int64(time.Hour * 24 * 7) | |||
| PresignedUploadPartUrlExpireTime = time.Hour * 24 * 7 | |||
| ) | |||
| type ComplPart struct { | |||
| @@ -333,12 +333,17 @@ func UpdateAttachmentDecompressState(ctx *context.Context) { | |||
| func GetSuccessChunks(ctx *context.Context) { | |||
| fileMD5 := ctx.Params("fileMD5") | |||
| fileMD5 := ctx.Query("md5") | |||
| fileChunk, err := models.GetFileChunkByMD5(fileMD5) | |||
| if err != nil { | |||
| if models.IsErrFileChunkNotExist(err) { | |||
| ctx.Error(404) | |||
| ctx.JSON(200, map[string]string{ | |||
| "uuid": "", | |||
| "uploaded": "0", | |||
| "uploadID": "", | |||
| "chunks": "", | |||
| }) | |||
| } else { | |||
| ctx.ServerError("GetFileChunkByMD5", err) | |||
| } | |||
| @@ -412,9 +417,9 @@ func NewMultipart(ctx *context.Context) { | |||
| func GetMultipartUploadUrl(ctx *context.Context) { | |||
| uuid := ctx.Query("uuid") | |||
| uploadID := ctx.Params("uploadID") | |||
| partNumber := ctx.ParamsInt("chunkNumber") | |||
| size := ctx.ParamsInt64("size") | |||
| uploadID := ctx.Query("uploadID") | |||
| partNumber := ctx.QueryInt("chunkNumber") | |||
| size := ctx.QueryInt64("size") | |||
| if size > minio_ext.MinPartSize { | |||
| ctx.Error(400, fmt.Sprintf("chunk size(%d) is too big", size)) | |||
| @@ -73,18 +73,117 @@ | |||
| // 计算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) { | |||
| console.log(response.data.uploadID, response.data.uuid, response.data.uploaded, response.data.chunks); | |||
| file.uploadID = response.data.uploadID; | |||
| file.uuid = response.data.uuid; | |||
| file.uploaded = response.data.uploaded; | |||
| file.chunks = response.data.chunks; | |||
| resolve(response); | |||
| }).catch(function (error) { | |||
| console.log(error); | |||
| reject(error); | |||
| }); | |||
| }) | |||
| }, | |||
| newMultiUpload(file) { | |||
| axios.get('/attachments/new_multipart', {params :{ | |||
| totalChunkCounts: file.totalChunkCounts, | |||
| md5: file.uniqueIdentifier, | |||
| size: file.size, | |||
| fileType: file.fileType, | |||
| _csrf: csrf | |||
| }}).then(function (response) { | |||
| console.log(response.data.uploadID, response.data.uuid); | |||
| }).catch(function (error) { | |||
| console.log(error); | |||
| }); | |||
| 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) { | |||
| console.log(response.data.uploadID, response.data.uuid); | |||
| 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*128, | |||
| 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 uploadChunk(e) { | |||
| //判断是否已上传 | |||
| if (!checkSuccessChunks()) { | |||
| let start = currentChunk * chunkSize; | |||
| let partSize = ((start + chunkSize) >= file.size) ? file.size -start : chunkSize; | |||
| //获取分片url | |||
| axios.get('/attachments/get_multipart_url', {params :{ | |||
| uuid: file.uuid, | |||
| uploadID: file.uploadID, | |||
| size: partSize, | |||
| chunkNumber: currentChunk+1, | |||
| _csrf: csrf | |||
| }}).then(function (response) { | |||
| //todo:分片上传 | |||
| axios.put(response.data.url, e.target.result | |||
| ).then(function (res) { | |||
| console.log(res.headers.etag); | |||
| }).catch(function (error) { | |||
| console.log(error); | |||
| }); | |||
| }).catch(function (error) { | |||
| console.log(error); | |||
| }); | |||
| } | |||
| } | |||
| var successChunks = new Array(); | |||
| successChunks = file.chunks.split(","); | |||
| console.log('上传分片...'); | |||
| loadNext(); | |||
| fileReader.onload = (e) => { | |||
| uploadChunk(e); | |||
| currentChunk++; | |||
| if (currentChunk < chunks && !checkSuccessChunks()) { | |||
| console.log(`第${currentChunk}个分片上传完成, 开始第${currentChunk +1} / ${chunks}个分片上传`); | |||
| loadNext(); | |||
| } else { | |||
| console.log(`文件上传完成:${file.name} \n分片:${chunks} 大小:${file.size} 用时:${(new Date().getTime() - time)/1000} s`); | |||
| //this.computeMD5Success(file); | |||
| } | |||
| }; | |||
| }, | |||
| chkMd5(file) { | |||
| let time = new Date().getTime(); | |||
| @@ -149,8 +248,27 @@ | |||
| fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end)); | |||
| } | |||
| }, | |||
| computeMD5Success(file) { | |||
| this.newMultiUpload(file) | |||
| async computeMD5Success(file) { | |||
| await this.getSuccessChunks(file); | |||
| if (file.uploadID == "" || file.uuid == "") { //未上传过 | |||
| console.log("test1"); | |||
| await this.newMultiUpload(file); | |||
| if (file.uploadID != "" && file.uuid != "") { | |||
| file.chunks = ""; | |||
| //todo:开始分片上传:分片,获取分片上传地址,上传 | |||
| this.multipartUpload(file); | |||
| } else { | |||
| return; | |||
| } | |||
| } else { | |||
| console.log("test2"); | |||
| if (file.uploaded == "1") { //已上传成功 | |||
| //todo:结束上传 | |||
| } else { | |||
| //todo:查询已上传成功的分片,重新上传未成功上传的分片 | |||
| } | |||
| } | |||
| }, | |||
| // 文件进度的回调 | |||
| onFileProgress(rootFile, file, chunk) { | |||
| @@ -1,65 +0,0 @@ | |||
| @font-face {font-family: "iconfont"; | |||
| src: url('iconfont.eot?t=1585640036938'); /* IE9 */ | |||
| src: url('iconfont.eot?t=1585640036938#iefix') format('embedded-opentype'), /* IE6-IE8 */ | |||
| url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'), | |||
| url('iconfont.woff?t=1585640036938') format('woff'), | |||
| url('iconfont.ttf?t=1585640036938') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ | |||
| url('iconfont.svg?t=1585640036938#iconfont') format('svg'); /* iOS 4.1- */ | |||
| } | |||
| .iconfont { | |||
| font-family: "iconfont" !important; | |||
| font-size: 16px; | |||
| font-style: normal; | |||
| -webkit-font-smoothing: antialiased; | |||
| -moz-osx-font-smoothing: grayscale; | |||
| } | |||
| .icon-zip:before { | |||
| content: "\e626"; | |||
| } | |||
| .icon-unknown:before { | |||
| content: "\e613"; | |||
| } | |||
| .icon-audio:before { | |||
| content: "\e645"; | |||
| } | |||
| .icon-image:before { | |||
| content: "\e656"; | |||
| } | |||
| .icon-video:before { | |||
| content: "\e617"; | |||
| } | |||
| .icon-selimage:before { | |||
| content: "\e724"; | |||
| } | |||
| .icon-dir:before { | |||
| content: "\e761"; | |||
| } | |||
| .icon-tianjia:before { | |||
| content: "\e61f"; | |||
| } | |||
| .icon-mkdir:before { | |||
| content: "\e607"; | |||
| } | |||
| .icon-upload:before { | |||
| content: "\e614"; | |||
| } | |||
| .icon-document:before { | |||
| content: "\e760"; | |||
| } | |||
| .icon-folder:before { | |||
| content: "\e686"; | |||
| } | |||
| @@ -1,93 +0,0 @@ | |||
| { | |||
| "id": "1720184", | |||
| "name": "upload", | |||
| "font_family": "iconfont", | |||
| "css_prefix_text": "icon-", | |||
| "description": "", | |||
| "glyphs": [ | |||
| { | |||
| "icon_id": "6049876", | |||
| "name": "压缩包", | |||
| "font_class": "zip", | |||
| "unicode": "e626", | |||
| "unicode_decimal": 58918 | |||
| }, | |||
| { | |||
| "icon_id": "553902", | |||
| "name": "文档", | |||
| "font_class": "unknown", | |||
| "unicode": "e613", | |||
| "unicode_decimal": 58899 | |||
| }, | |||
| { | |||
| "icon_id": "1004653", | |||
| "name": "文件-音频", | |||
| "font_class": "audio", | |||
| "unicode": "e645", | |||
| "unicode_decimal": 58949 | |||
| }, | |||
| { | |||
| "icon_id": "1071023", | |||
| "name": "图像1", | |||
| "font_class": "image", | |||
| "unicode": "e656", | |||
| "unicode_decimal": 58966 | |||
| }, | |||
| { | |||
| "icon_id": "1638517", | |||
| "name": "视频", | |||
| "font_class": "video", | |||
| "unicode": "e617", | |||
| "unicode_decimal": 58903 | |||
| }, | |||
| { | |||
| "icon_id": "13137449", | |||
| "name": "图像_未选择", | |||
| "font_class": "selimage", | |||
| "unicode": "e724", | |||
| "unicode_decimal": 59172 | |||
| }, | |||
| { | |||
| "icon_id": "4933421", | |||
| "name": "文件夹", | |||
| "font_class": "dir", | |||
| "unicode": "e761", | |||
| "unicode_decimal": 59233 | |||
| }, | |||
| { | |||
| "icon_id": "521050", | |||
| "name": "添加", | |||
| "font_class": "tianjia", | |||
| "unicode": "e61f", | |||
| "unicode_decimal": 58911 | |||
| }, | |||
| { | |||
| "icon_id": "602556", | |||
| "name": "网盘-新建文件夹", | |||
| "font_class": "mkdir", | |||
| "unicode": "e607", | |||
| "unicode_decimal": 58887 | |||
| }, | |||
| { | |||
| "icon_id": "2305613", | |||
| "name": "上传文件", | |||
| "font_class": "upload", | |||
| "unicode": "e614", | |||
| "unicode_decimal": 58900 | |||
| }, | |||
| { | |||
| "icon_id": "4933419", | |||
| "name": "文件", | |||
| "font_class": "document", | |||
| "unicode": "e760", | |||
| "unicode_decimal": 59232 | |||
| }, | |||
| { | |||
| "icon_id": "6583375", | |||
| "name": "文件", | |||
| "font_class": "folder", | |||
| "unicode": "e686", | |||
| "unicode_decimal": 59014 | |||
| } | |||
| ] | |||
| } | |||
| @@ -1,62 +0,0 @@ | |||
| <?xml version="1.0" standalone="no"?> | |||
| <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > | |||
| <!-- | |||
| 2013-9-30: Created. | |||
| --> | |||
| <svg> | |||
| <metadata> | |||
| Created by iconfont | |||
| </metadata> | |||
| <defs> | |||
| <font id="iconfont" horiz-adv-x="1024" > | |||
| <font-face | |||
| font-family="iconfont" | |||
| font-weight="500" | |||
| font-stretch="normal" | |||
| units-per-em="1024" | |||
| ascent="896" | |||
| descent="-128" | |||
| /> | |||
| <missing-glyph /> | |||
| <glyph glyph-name="zip" unicode="" d="M937.984 793.6H91.136c-46.08 0-83.456-37.376-83.456-83.456v-650.752c0-46.08 37.376-83.456 83.456-83.456h846.848c46.08 0 83.456 37.376 83.456 83.456V710.144c0 46.08-37.376 83.456-83.456 83.456z m22.016-734.208c0-12.288-9.728-22.016-22.016-22.016H91.136c-12.288 0-22.016 9.728-22.016 22.016V710.144c0 12.288 9.728 22.016 22.016 22.016H332.8v-91.648h96.768V732.16h508.416c12.288 0 22.016-9.728 22.016-22.016v-650.752zM429.568 638.976h96.256V542.72H429.568zM333.312 542.72h96.256v-96.256H333.312zM431.104 444.928H527.36v-96.256H431.104zM333.824 347.648H430.08v-96.256H333.824z" horiz-adv-x="1024" /> | |||
| <glyph glyph-name="unknown" unicode="" d="M678.4 840.533333l-64 0L221.866667 840.533333c-46.933333 0-85.333333-38.4-85.333333-85.333333l0-750.933333c0-46.933333 38.4-85.333333 85.333333-85.333333l584.533333 0c46.933333 0 85.333333 38.4 85.333333 85.333333L891.733333 571.733333l0 42.666667L678.4 840.533333zM844.8 8.53333299999997c0-21.333333-17.066667-42.666667-42.666667-42.666667L221.866667-34.13333299999999c-21.333333 0-42.666667 17.066667-42.666667 42.666667L179.2 759.466667c0 21.333333 17.066667 42.666667 42.666667 42.666667L597.333333 802.133333l0-209.066667c0-21.333333 17.066667-42.666667 42.666667-42.666667l209.066667 0L849.066667 8.53333299999997zM635.733333 593.066667L635.733333 802.133333l21.333333 0 187.733333-209.066667L635.733333 593.066667z" horiz-adv-x="1024" /> | |||
| <glyph glyph-name="audio" unicode="" d="M877.854 626.775l-56.805 56.806-121.726 123.079c-24.345 21.64-41.928 27.05-68.978 27.05h-451.737c-31.108 0-55.453-24.345-55.453-55.453v-789.865c0-29.755 24.345-54.1 55.453-54.1h666.787c31.108 0 55.453 24.345 55.453 54.1v584.284c0 24.345-8.115 35.165-22.993 54.1v0zM830.516 606.4870000000001h-156.891v156.891l156.891-156.891zM856.213-11.609000000000037c0-5.409-4.057-10.821-10.821-10.821h-666.787c-6.762 0-12.172 5.409-12.172 10.821v789.865c0 6.762 5.409 12.172 12.172 12.172 0 0 451.737 0 451.737 0v-205.582c0-12.173 9.468-21.64 21.64-21.64h204.229v-574.816zM723.668 482.057c-117.668 1.353-246.157-22.993-363.825-59.511-9.468-4.058-10.821-5.409-10.821-14.877v-210.991c-12.172 5.409-27.05 6.762-41.927 5.409-45.985-1.353-82.503-29.755-82.503-60.862 0-31.108 36.517-55.453 82.503-52.748 45.985 2.706 82.503 29.755 82.503 60.863v193.409c109.553 25.698 209.638 43.28 312.429 51.395v-150.128c-12.173 5.409-25.698 6.762-40.576 6.762-45.985-2.706-82.503-29.755-82.503-62.215 0-31.108 36.517-55.453 82.503-52.748 44.632 2.706 82.503 29.755 82.503 60.863v267.797c0 13.525-6.762 16.23-20.287 17.583z" horiz-adv-x="1024" /> | |||
| <glyph glyph-name="image" unicode="" d="M871.898 772.915L149.702 772.915c-46.045 0-83.375-37.327-83.375-83.375l0-611.078c0-46.04 37.33-83.37 83.375-83.37l722.186 0c46.05 0 83.38 37.33 83.38 83.37L955.268 689.5409999999999C955.268 735.585 917.938 772.915 871.898 772.915L871.898 772.915 871.898 772.915zM149.682 717.358l722.236 0c15.35 0 27.79-12.45 27.79-27.792l0-327.194c-44.77 36.747-122.38 86.27-215.479 86.27-85.85 0-170.482-66.522-252.319-130.86-62.712-49.28-127.562-100.26-173.06-100.26-63.822 0-118.447 46.92-136.965 64.83L121.885 689.566C121.885 704.908 134.335 717.358 149.682 717.358L149.682 717.358 149.682 717.358zM871.918 50.652000000000044L149.682 50.652000000000044c-15.347 0-27.797 12.44-27.797 27.79l0 131.87c33.315-23.6 81.482-48.37 136.967-48.37 64.727 0 134.032 54.48 207.399 112.14 74.417 58.49 151.357 118.975 217.977 118.975 119.4 0 214.099-105.105 215.039-106.175l0.44 0.39 0-208.829C899.708 63.091999999999985 887.268 50.652000000000044 871.918 50.652000000000044L871.918 50.652000000000044 871.918 50.652000000000044zM343.839 383.701c61.455 0 111.267 49.815 111.267 111.267s-49.812 111.267-111.267 111.267c-61.452 0-111.267-49.815-111.267-111.267S282.387 383.701 343.839 383.701L343.839 383.701 343.839 383.701zM344.257 550.681c30.595 0 55.395-24.8 55.395-55.4s-24.8-55.395-55.395-55.395c-30.6 0-55.402 24.795-55.402 55.395C288.854 525.876 313.662 550.681 344.257 550.681L344.257 550.681 344.257 550.681z" horiz-adv-x="1024" /> | |||
| <glyph glyph-name="video" unicode="" d="M602.224 5h-480.96C54.392 5 0 59.360000000000014 0 126.26400000000001v515.44c0 66.872 54.392 121.304 121.264 121.304h480.96c66.864 0 121.296-54.424 121.296-121.304v-88.312l253.032 146.104a31.76 31.76 0 0 0 31.632 0A31.632 31.632 0 0 0 1024 672.096v-576.232a31.704 31.704 0 0 0-15.816-27.424 31.76 31.76 0 0 0-31.632 0L723.52 214.53599999999994v-88.272c0-66.904-54.424-121.264-121.296-121.264z m-480.96 694.744c-32 0-58.008-26.04-58.008-58.04v-515.44c0-32 26.008-58.008 58.008-58.008h480.96c32 0 58.04 26.008 58.04 58.008V269.336c0 11.304 6.024 21.752 15.816 27.432a31.744 31.744 0 0 0 31.624 0l253.04-146.104V617.312l-253.04-146.104a31.744 31.744 0 0 0-31.624 0 31.632 31.632 0 0 0-15.816 27.4V641.704c0 32-26.04 58.04-58.04 58.04h-480.96z" horiz-adv-x="1024" /> | |||
| <glyph glyph-name="selimage" unicode="" d="M844.75904 750.5510400000001A76.94336 76.94336 0 0 0 921.6 673.4848v-564.77696a76.94336 76.94336 0 0 0-76.84096-77.04576H179.24096A76.94336 76.94336 0 0 0 102.4 108.70784000000003V673.4848a76.94336 76.94336 0 0 0 76.84096 77.06624h665.51808z m-100.5568-337.14176c-22.46656 0-54.90688-19.53792-122.34752-71.8848l-33.15712-25.94816c-110.12096-85.85216-164.10624-115.75296-236.52352-115.75296-68.34176 0-131.76832 24.63744-188.33408 64.1024v-155.21792c0-7.92576 5.81632-14.4384 13.312-15.4624l2.08896-0.14336h665.51808a15.48288 15.48288 0 0 1 15.2576 13.49632l0.14336 2.10944V355.26656a30.59712 30.59712 0 0 0-11.14112 6.92224c-29.92128 28.99968-67.93216 51.22048-104.83712 51.22048z m100.5568 275.70176H179.24096a15.48288 15.48288 0 0 1-15.2576-13.49632L163.84 673.4848l0.02048-334.25408c3.2768-1.16736 6.41024-2.90816 9.25696-5.24288 53.76-44.2368 115.05664-72.72448 179.05664-72.72448 53.4528 0 100.74112 26.37824 199.39328 103.28064l20.6848 16.22016c90.58304 71.12704 127.63136 94.08512 171.9296 94.08512 42.88512 0 82.04288-16.71168 115.99872-41.69728L860.16 673.4848a15.54432 15.54432 0 0 1-13.312 15.48288l-2.08896 0.14336zM348.30336 609.28c56.6272 0 102.54336-46.03904 102.54336-102.83008 0-56.81152-45.91616-102.85056-102.54336-102.85056-56.6272 0-102.54336 46.03904-102.54336 102.85056C245.76 563.24096 291.67616 609.28 348.30336 609.28z m0-61.44A41.24672 41.24672 0 0 1 307.2 506.44992c0-22.91712 18.432-41.41056 41.10336-41.41056a41.24672 41.24672 0 0 1 41.10336 41.41056c0 22.89664-18.432 41.39008-41.10336 41.39008z" horiz-adv-x="1024" /> | |||
| <glyph glyph-name="dir" unicode="" d="M510.4 652.8c8-27.2 33.6-44.8 60.8-44.8h259.2c0 35.2-28.8 64-64 64H504l6.4-19.2zM484.8 736h281.6c70.4 0 128-57.6 128-128v-25.6c30.4-24 51.2-60.8 51.2-102.4v-384c0-70.4-57.6-128-128-128H208c-70.4 0-128 57.6-128 128V672c0 70.4 57.6 128 128 128h164.8c46.4 0 89.6-25.6 112-64z m-112 0H208c-35.2 0-64-28.8-64-64v-576c0-35.2 28.8-64 64-64h608c35.2 0 64 28.8 64 64V480c0 35.2-28.8 64-64 64H574.4c-56 0-105.6 36.8-121.6 89.6l-19.2 57.6c-8 27.2-32 44.8-60.8 44.8zM272 192h256c17.6 0 32-14.4 32-32s-14.4-32-32-32H272c-17.6 0-32 14.4-32 32s14.4 32 32 32z" horiz-adv-x="1024" /> | |||
| <glyph glyph-name="tianjia" unicode="" d="M212.992 369.664 212.992 369.664 212.992 369.664 215.04 369.664 212.992 369.664ZM69.632 816.128l0-866.304 868.352 0L937.984 816.128 69.632 816.128zM720.896 334.848 552.96 334.848l0-167.936c0-28.672-22.528-51.2-49.152-51.2-28.672 0-51.2 22.528-51.2 51.2l0 167.936L286.72 334.848c-28.672 0-51.2 22.528-51.2 51.2 0 28.672 22.528 49.152 51.2 49.152l165.888 0 0 167.936c0 28.672 22.528 51.2 51.2 51.2 26.624 0 49.152-22.528 49.152-51.2l0-167.936 167.936 0c28.672 0 51.2-20.48 51.2-49.152C770.048 355.328 747.52 334.848 720.896 334.848z" horiz-adv-x="1024" /> | |||
| <glyph glyph-name="mkdir" unicode="" d="M1018.23488 13.335893000000056c0 0 0 559.342933 0 621.01504 0 83.749547-76.26752 75.97056-76.26752 75.97056s-363.779413-0.539307-339.69152 0c-26.170027-0.58368-39.03488 13.70112-39.03488 13.70112s-18.10432 31.20128-50.72896 80.530773c-34.194773 51.725653-73.786027 43.277653-73.786027 43.277653L102.85056 847.83104c-93.057707 0-94.08512-89.51808-94.08512-89.51808s0-675.874133 0-740.3008c0-99.853653 75.421013-87.487147 75.421013-87.487147s807.857493 0 864.679253 0C1029.533013-69.47157300000003 1018.23488 13.335893000000056 1018.23488 13.335893000000056zM857.091413 22.312959999999975c-56.825173 0-681.12384 0-681.12384 0s-75.424427-12.36992-75.424427 87.487147c0 64.41984 0 556.73856 0 556.73856s1.030827 89.51808 94.088533 89.51808l175.264427 0c0 0 39.594667 8.448 73.78944-43.281067 32.62464-49.329493 50.72896-80.52736 50.72896-80.52736s12.858027-14.288213 39.031467-13.704533c-24.087893-0.535893 316.740267 0 316.740267 0s76.27776 7.775573 76.27776-75.97056c0-61.672107 0-437.46304 0-437.46304S937.751893 22.312959999999975 857.091413 22.312959999999975zM685.605547 342.920533l-149.138773 0 0 149.138773c0 6.331733-5.13024 11.472213-11.472213 11.472213l-22.944427 0c-6.33856 0-11.472213-5.14048-11.472213-11.472213l0-149.138773-149.138773 0c-6.341973 0-11.472213-5.143893-11.472213-11.472213l0-22.94784c0-6.335147 5.126827-11.461973 11.472213-11.461973l149.138773 0 0-149.1456c0-6.341973 5.133653-11.472213 11.472213-11.472213l22.944427 0c6.341973 0 11.472213 5.13024 11.472213 11.472213l0 149.1456 149.138773 0c6.341973 0 11.475627 5.126827 11.475627 11.461973l0 22.94784C697.084587 337.77664000000004 691.950933 342.920533 685.605547 342.920533z" horiz-adv-x="1024" /> | |||
| <glyph glyph-name="upload" unicode="" d="M532.352 800c-138.688 0-260.992-88-312.32-218.304C111.808 558.656 32 458.752 32 341.312c0-135.168 105.152-245.312 235.648-245.312a32 32 0 1 1 0 64C173.184 160 96 240.832 96 341.312c0 92.608 65.92 169.664 151.68 180.16a32 32 0 0 1 26.496 21.632C312.32 657.92 415.424 736 532.352 736c145.6 0 265.92-120.32 273.152-273.472a32 32 0 0 1 23.68-29.44c57.472-15.296 98.816-70.336 98.816-134.4 0-76.928-58.944-138.688-130.88-138.688a32 32 0 1 1 0-64c107.968 0 194.88 91.072 194.88 202.688 0 84.672-50.432 159.104-124.288 188.928C848.768 663.6800000000001 705.6 800 532.352 800z m11.648-557.248V-32a32 32 0 1 0-64 0v274.752l-73.344-73.408a32 32 0 0 0-45.312 45.312l128 128a32 32 0 0 0 45.312 0l128-128a32 32 0 0 0-45.312-45.312L544 242.75199999999995z" horiz-adv-x="1024" /> | |||
| <glyph glyph-name="document" unicode="" d="M752 816H272c-70.4 0-128-57.6-128-128v-608c0-70.4 57.6-128 128-128h353.6c33.6 0 65.6 12.8 91.2 36.8l126.4 126.4c24 24 36.8 56 36.8 91.2V688c0 70.4-57.6 128-128 128zM208 80V688c0 35.2 28.8 64 64 64h480c35.2 0 64-28.8 64-64v-464h-96c-70.4 0-128-57.6-128-128v-80H272c-35.2 0-64 28.8-64 64z m462.4-44.8c-4.8-4.8-9.6-8-14.4-11.2v72c0 35.2 28.8 64 64 64h75.2L670.4 35.200000000000045zM368 544h288c17.6 0 32 14.4 32 32s-14.4 32-32 32H368c-17.6 0-32-14.4-32-32s14.4-32 32-32z m128-256H368c-17.6 0-32-14.4-32-32s14.4-32 32-32h128c17.6 0 32 14.4 32 32s-14.4 32-32 32z m-128 96h288c17.6 0 32 14.4 32 32s-14.4 32-32 32H368c-17.6 0-32-14.4-32-32s14.4-32 32-32z" horiz-adv-x="1024" /> | |||
| <glyph glyph-name="folder" unicode="" d="M550.4 618.666667h260.266667c56.554667 0 102.4-45.845333 102.4-102.4v-375.466667c0-56.554667-45.845333-102.4-102.4-102.4H213.333333c-56.554667 0-102.4 45.845333-102.4 102.4V661.333333c0 37.704533 30.562133 68.266667 68.266667 68.266667h264.789333a68.266667 68.266667 0 0 0 58.1376-32.4864L550.4 618.666667z m259.84-528.695467c28.279467 0 51.2 22.9248 51.2 51.2v375.466667c0 28.2752-22.920533 51.2-51.2 51.2H162.56v-426.666667c0-28.2752 22.9248-51.2 51.2-51.2h596.48zM465.7152 669.802667A17.066667 17.066667 0 0 1 450.897067 678.4H179.2a17.066667 17.066667 0 0 1-17.066667-17.066667v-41.634133L494.933333 618.666667l-29.218133 51.136z" horiz-adv-x="1024" /> | |||
| </font> | |||
| </defs></svg> | |||
| @@ -1,230 +0,0 @@ | |||
| <template> | |||
| <div id="page"> | |||
| <header> | |||
| <h1 class="logo"><a href="http://www.helloweba.net" title="返回helloweba首页">helloweba</a></h1> | |||
| </header> | |||
| <div class="main"> | |||
| <h2><a href="http://www.helloweba.net/javascript/637.html">文件上传之暂停和断点续传和跨浏览器续传</a></h2> | |||
| <uploader | |||
| ref="uploader" | |||
| :options="options" | |||
| :file-status-text="fileStatusText" | |||
| :autoStart="false" | |||
| @file-added="onFileAdded" | |||
| @file-progress="onFileProgress" | |||
| @file-success="onFileSuccess" | |||
| @file-error="onFileError" | |||
| class="uploader"> | |||
| <uploader-unsupport></uploader-unsupport> | |||
| <uploader-drop> | |||
| <uploader-btn class="upfile"><i class="iconfont icon-upload"></i> 上传文件</uploader-btn> | |||
| <uploader-btn class="updir" :directory="true"><i class="iconfont icon-dir"></i> 上传文件夹</uploader-btn> | |||
| </uploader-drop> | |||
| <uploader-list></uploader-list> | |||
| </uploader> | |||
| </div> | |||
| <footer> | |||
| <p>Powered by helloweba.net 允许转载、修改和使用本站的DEMO,但请注明出处:<a href="http://www.helloweba.net">www.helloweba.net</a></p> | |||
| <p class="hidden"></p> | |||
| </footer> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| import axios from 'axios'; | |||
| import SparkMD5 from 'spark-md5'; | |||
| export default { | |||
| data () { | |||
| return { | |||
| options: { | |||
| target: 'http://localhost:9999/up.php', | |||
| chunkSize: 2097152, //2MB | |||
| simultaneousUploads: 1, //并发上传数 | |||
| headers: { | |||
| 'X-token': 'abcd123' | |||
| }, | |||
| maxChunkRetries: 2, //最大自动失败重试上传次数 | |||
| parseTimeRemaining: function (timeRemaining, parsedTimeRemaining) { //格式化时间 | |||
| return parsedTimeRemaining | |||
| .replace(/\syears?/, '年') | |||
| .replace(/\days?/, '天') | |||
| .replace(/\shours?/, '小时') | |||
| .replace(/\sminutes?/, '分钟') | |||
| .replace(/\sseconds?/, '秒') | |||
| }, | |||
| testChunks: true, //开启服务端分片校验 | |||
| // 服务器分片校验函数 | |||
| checkChunkUploadedByResponse: (chunk, message) => { | |||
| let obj = JSON.parse(message); | |||
| if (obj.isExist) { | |||
| this.statusTextMap.success = '秒传文件'; | |||
| return true; | |||
| } | |||
| return (obj.uploaded || []).indexOf(chunk.offset + 1) >= 0 | |||
| }, | |||
| }, | |||
| statusTextMap: { | |||
| success: '上传成功', | |||
| error: '上传出错了', | |||
| uploading: '上传中...', | |||
| paused: '暂停', | |||
| waiting: '等待中...', | |||
| cmd5: '计算md5...' | |||
| }, | |||
| fileStatusText: (status, response) => { | |||
| return this.statusTextMap[status]; | |||
| }, | |||
| } | |||
| }, | |||
| created() { | |||
| // | |||
| }, | |||
| methods: { | |||
| onFileAdded(file) { | |||
| // 计算MD5 | |||
| this.computeMD5(file); | |||
| }, | |||
| //计算MD5 | |||
| computeMD5(file) { | |||
| let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice, | |||
| chunkSize = 2097152, | |||
| chunks = Math.ceil(file.size / chunkSize), | |||
| currentChunk = 0, | |||
| spark = new SparkMD5.ArrayBuffer(), | |||
| fileReader = new FileReader(); | |||
| let time = new Date().getTime(); | |||
| file.cmd5 = true; | |||
| fileReader.onload = (e) => { | |||
| spark.append(e.target.result); // Append array buffer | |||
| currentChunk++; | |||
| if (currentChunk < chunks) { | |||
| //console.log(`第${currentChunk}分片解析完成, 开始第${currentChunk +1} / ${chunks}分片解析`); | |||
| let percent = Math.floor(currentChunk / chunks * 100); | |||
| file.cmd5progress = percent; | |||
| loadNext(); | |||
| } else { | |||
| console.log('finished loading'); | |||
| let md5 = spark.end(); | |||
| console.log(`MD5计算完成:${file.name} \nMD5:${md5} \n分片:${chunks} 大小:${file.size} 用时:${new Date().getTime() - time} ms`); | |||
| spark.destroy(); //释放缓存 | |||
| file.uniqueIdentifier = md5; //将文件md5赋值给文件唯一标识 | |||
| file.cmd5 = false; //取消计算md5状态 | |||
| file.resume(); //开始上传 | |||
| } | |||
| }; | |||
| fileReader.onerror = () => { | |||
| console.warn('oops, something went wrong.'); | |||
| file.cancel(); | |||
| }; | |||
| let loadNext = () => { | |||
| let start = currentChunk * chunkSize, | |||
| end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize; | |||
| fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end)); | |||
| }; | |||
| loadNext(); | |||
| }, | |||
| // 文件进度的回调 | |||
| onFileProgress(rootFile, file, chunk) { | |||
| console.log(`上传中 ${file.name},chunk:${chunk.startByte / 1024 / 1024} ~ ${chunk.endByte / 1024 / 1024}`) | |||
| }, | |||
| onFileSuccess(rootFile, file, response, chunk) { | |||
| let resp = JSON.parse(response); | |||
| //合并分片 | |||
| if (resp.code === 0 && resp.merge === true) { | |||
| axios.post('http://localhost:9999/up.php?action=merge', { | |||
| filename: file.name, | |||
| identifier: file.uniqueIdentifier, | |||
| totalSize: file.size, | |||
| totalChunks: chunk.offset + 1 | |||
| }).then(function(res){ | |||
| if (res.code === 0) { | |||
| console.log('上传成功') | |||
| } else { | |||
| console.log(res.message); | |||
| } | |||
| }) | |||
| .catch(function(error){ | |||
| console.log(error); | |||
| }); | |||
| } | |||
| }, | |||
| onFileError(rootFile, file, response, chunk) { | |||
| console.log('Error:', response) | |||
| }, | |||
| } | |||
| } | |||
| </script> | |||
| <style scoped lang="less"> | |||
| .main{ | |||
| max-width: 1000px; | |||
| margin: 10px auto; | |||
| background: #fff; | |||
| padding: 10px; | |||
| h2{ | |||
| padding: 30px 0; | |||
| text-align: center; | |||
| font-size: 20px; | |||
| } | |||
| } | |||
| .uploader { | |||
| width: 880px; | |||
| padding: 15px; | |||
| margin: 20px auto 0; | |||
| font-size: 14px; | |||
| box-shadow: 0 0 10px rgba(0, 0, 0, .4); | |||
| .uploader-btn { | |||
| margin-right: 4px; | |||
| color: #fff; | |||
| padding: 6px 16px; | |||
| } | |||
| .upfile{ | |||
| border: 1px solid #409eff; | |||
| background: #409eff; | |||
| } | |||
| .updir{ | |||
| border: 1px solid #67c23a; | |||
| background: #67c23a; | |||
| } | |||
| .uploader-list { | |||
| max-height: 440px; | |||
| overflow: auto; | |||
| overflow-x: hidden; | |||
| overflow-y: auto; | |||
| height: 356px; | |||
| /deep/.iconfont { | |||
| font-size: 18px; | |||
| color: #409eff; | |||
| } | |||
| } | |||
| } | |||
| //手机等小屏幕手持设备。当设备宽度 在 320px和768px之间时,执行当前的css | |||
| @media only screen and (min-width: 320px) and (max-width: 768px) { | |||
| .uploader { | |||
| width: 98%; | |||
| padding: 0; | |||
| box-shadow: none; | |||
| } | |||
| } | |||
| </style> | |||
| @@ -1,153 +0,0 @@ | |||
| <template> | |||
| <div id="page"> | |||
| <header> | |||
| <h1 class="logo"><a href="http://www.helloweba.net" title="返回helloweba首页">helloweba</a></h1> | |||
| </header> | |||
| <div class="main"> | |||
| <h2><a href="http://www.helloweba.net/javascript/633.html">大文件上传之分片上传</a></h2> | |||
| <uploader | |||
| ref="uploader" | |||
| :options="options" | |||
| :fileStatusText="fileStatusText" | |||
| @file-progress="onFileProgress" | |||
| @file-success="onFileSuccess" | |||
| @file-error="onFileError" | |||
| class="uploader"> | |||
| <uploader-unsupport></uploader-unsupport> | |||
| <uploader-drop> | |||
| <uploader-btn class="upfile"><i class="iconfont icon-upload"></i> 上传文件</uploader-btn> | |||
| <uploader-btn class="updir" :directory="true"><i class="iconfont icon-dir"></i> 上传文件夹</uploader-btn> | |||
| </uploader-drop> | |||
| <uploader-list></uploader-list> | |||
| </uploader> | |||
| </div> | |||
| <footer> | |||
| <p>Powered by helloweba.net 允许转载、修改和使用本站的DEMO,但请注明出处:<a href="http://www.helloweba.net">www.helloweba.net</a></p> | |||
| <p class="hidden"></p> | |||
| </footer> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| import axios from 'axios'; | |||
| export default { | |||
| data () { | |||
| return { | |||
| options: { | |||
| target: 'up.php', | |||
| testChunks: false, | |||
| chunkSize: 1024*1024*2, //2MB | |||
| simultaneousUploads: 1, //并发上传数 | |||
| headers: { | |||
| 'X-token': 'abcd123' | |||
| }, | |||
| maxChunkRetries: 1, //最大自动失败重试上传次数 | |||
| parseTimeRemaining: function (timeRemaining, parsedTimeRemaining) { //格式化时间 | |||
| return parsedTimeRemaining | |||
| .replace(/\syears?/, '年') | |||
| .replace(/\days?/, '天') | |||
| .replace(/\shours?/, '小时') | |||
| .replace(/\sminutes?/, '分钟') | |||
| .replace(/\sseconds?/, '秒') | |||
| } | |||
| }, | |||
| fileStatusText: { | |||
| success: '上传成功', | |||
| error: '上传出错了', | |||
| uploading: '上传中...', | |||
| paused: '暂停', | |||
| waiting: '等待中...' | |||
| }, | |||
| } | |||
| }, | |||
| created() { | |||
| }, | |||
| methods: { | |||
| // 文件进度的回调 | |||
| onFileProgress(rootFile, file, chunk) { | |||
| console.log(`上传中 ${file.name},chunk:${chunk.startByte / 1024 / 1024} ~ ${chunk.endByte / 1024 / 1024}`) | |||
| if (file.size > 1024 * 1024 * 10) { | |||
| alert('文件太大'); | |||
| file.cancel(); | |||
| } | |||
| }, | |||
| onFileSuccess(rootFile, file, response, chunk) { | |||
| let resp = JSON.parse(response); | |||
| if (resp.code === 0 && resp.merge === false) { | |||
| console.log('上传成功,不需要合并'); | |||
| } else { | |||
| axios.post('up.php?action=merge', { | |||
| filename: file.name, | |||
| identifier: file.uniqueIdentifier, | |||
| totalSize: file.size, | |||
| totalChunks: chunk.offset + 1 | |||
| }).then(function(res){ | |||
| if (res.code === 0) { | |||
| console.log('上传成功') | |||
| } else { | |||
| console.log(res.message); | |||
| } | |||
| }) | |||
| .catch(function(error){ | |||
| console.log(error); | |||
| }); | |||
| } | |||
| }, | |||
| onFileError(rootFile, file, response, chunk) { | |||
| alert(response); | |||
| file.cancel(); | |||
| console.log('Error:', response) | |||
| }, | |||
| } | |||
| } | |||
| </script> | |||
| <style scoped lang="less"> | |||
| .main{ | |||
| max-width: 1000px; | |||
| margin: 10px auto; | |||
| background: #fff; | |||
| padding: 10px; | |||
| h2{ | |||
| padding: 30px 0; | |||
| text-align: center; | |||
| font-size: 20px; | |||
| } | |||
| } | |||
| .uploader { | |||
| width: 880px; | |||
| padding: 15px; | |||
| margin: 20px auto 0; | |||
| font-size: 14px; | |||
| box-shadow: 0 0 10px rgba(0, 0, 0, .4); | |||
| .uploader-btn { | |||
| margin-right: 4px; | |||
| color: #fff; | |||
| padding: 6px 16px; | |||
| } | |||
| .upfile{ | |||
| border: 1px solid #409eff; | |||
| background: #409eff; | |||
| } | |||
| .updir{ | |||
| border: 1px solid #67c23a; | |||
| background: #67c23a; | |||
| } | |||
| } | |||
| //手机等小屏幕手持设备。当设备宽度 在 320px和768px之间时,执行当前的css | |||
| @media only screen and (min-width: 320px) and (max-width: 768px) { | |||
| .uploader { | |||
| width: 98%; | |||
| padding: 0; | |||
| box-shadow: none; | |||
| } | |||
| } | |||
| </style> | |||
| @@ -1,244 +0,0 @@ | |||
| <template> | |||
| <div id="page"> | |||
| <header> | |||
| <h1 class="logo"><a href="http://www.helloweba.net" title="返回helloweba首页">helloweba</a></h1> | |||
| </header> | |||
| <div class="main"> | |||
| <h2><a href="http://www.helloweba.net/javascript/635.html">超大文件上传和计算文件MD5值</a></h2> | |||
| <uploader | |||
| ref="uploader" | |||
| :options="options" | |||
| :file-status-text="fileStatusText" | |||
| :autoStart="false" | |||
| @file-added="onFileAdded" | |||
| @file-progress="onFileProgress" | |||
| @file-success="onFileSuccess" | |||
| @file-error="onFileError" | |||
| class="uploader"> | |||
| <uploader-unsupport></uploader-unsupport> | |||
| <uploader-drop> | |||
| <uploader-btn class="upfile"><i class="iconfont icon-upload"></i> 上传文件</uploader-btn> | |||
| <uploader-btn class="updir" :directory="true"><i class="iconfont icon-dir"></i> 上传文件夹</uploader-btn> | |||
| </uploader-drop> | |||
| <uploader-list></uploader-list> | |||
| </uploader> | |||
| </div> | |||
| <footer> | |||
| <p>Powered by helloweba.net 允许转载、修改和使用本站的DEMO,但请注明出处:<a href="http://www.helloweba.net">www.helloweba.net</a></p> | |||
| <p class="hidden"></p> | |||
| </footer> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| import axios from 'axios'; | |||
| import SparkMD5 from 'spark-md5'; | |||
| export default { | |||
| data () { | |||
| return { | |||
| options: { | |||
| target: 'http://localhost:9999/up.php', | |||
| testChunks: false, | |||
| chunkSize: 2097152, //2MB | |||
| simultaneousUploads: 1, //并发上传数 | |||
| headers: { | |||
| 'X-token': 'abcd123' | |||
| }, | |||
| maxChunkRetries: 2, //最大自动失败重试上传次数 | |||
| parseTimeRemaining: function (timeRemaining, parsedTimeRemaining) { //格式化时间 | |||
| return parsedTimeRemaining | |||
| .replace(/\syears?/, '年') | |||
| .replace(/\days?/, '天') | |||
| .replace(/\shours?/, '小时') | |||
| .replace(/\sminutes?/, '分钟') | |||
| .replace(/\sseconds?/, '秒') | |||
| } | |||
| }, | |||
| statusTextMap: { | |||
| success: '上传成功', | |||
| error: '上传出错了', | |||
| uploading: '上传中...', | |||
| paused: '暂停', | |||
| waiting: '等待中...', | |||
| cmd5: '计算md5...' | |||
| }, | |||
| fileStatusText: (status, response) => { | |||
| return this.statusTextMap[status]; | |||
| }, | |||
| } | |||
| }, | |||
| created() { | |||
| //const uploaderInstance = this.$refs.uploader.uploader; | |||
| }, | |||
| methods: { | |||
| onFileAdded(file) { | |||
| // 计算MD5 | |||
| this.computeMD5(file); | |||
| }, | |||
| chkMd5(file) { | |||
| let time = new Date().getTime(); | |||
| let fileReader = new FileReader(); | |||
| let spark = new SparkMD5(); //创建md5对象(基于SparkMD5) | |||
| fileReader.readAsBinaryString(file.file); | |||
| console.log('开始计算MD5...') | |||
| //文件读取完毕之后的处理 | |||
| fileReader.onload = (e) => { | |||
| spark.appendBinary(e.target.result); | |||
| let md5 = spark.end(); | |||
| console.log(`MD5计算完成:${file.name} \nMD5:${md5} \n用时:${new Date().getTime() - time} ms`); | |||
| spark.destroy(); | |||
| }; | |||
| }, | |||
| //计算MD5 | |||
| computeMD5(file) { | |||
| let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice, | |||
| chunkSize = 2097152, | |||
| chunks = Math.ceil(file.size / chunkSize), | |||
| currentChunk = 0, | |||
| spark = new SparkMD5.ArrayBuffer(), | |||
| fileReader = new FileReader(); | |||
| let time = new Date().getTime(); | |||
| // console.log('计算MD5...') | |||
| file.cmd5 = true; | |||
| fileReader.onload = (e) => { | |||
| spark.append(e.target.result); // Append array buffer | |||
| currentChunk++; | |||
| if (currentChunk < chunks) { | |||
| console.log(`第${currentChunk}分片解析完成, 开始第${currentChunk +1} / ${chunks}分片解析`); | |||
| // let percent = Math.floor(currentChunk / chunks * 100); | |||
| // console.log(percent); | |||
| // file.cmd5progress = percent; | |||
| loadNext(); | |||
| } else { | |||
| console.log('finished loading'); | |||
| let md5 = spark.end(); | |||
| console.log(`MD5计算完成:${file.name} \nMD5:${md5} \n分片:${chunks} 大小:${file.size} 用时:${new Date().getTime() - time} ms`); | |||
| spark.destroy(); //释放缓存 | |||
| file.uniqueIdentifier = md5; //将文件md5赋值给文件唯一标识 | |||
| file.cmd5 = false; //取消计算md5状态 | |||
| file.resume(); //开始上传 | |||
| console.log(file); | |||
| } | |||
| }; | |||
| fileReader.onerror = () => { | |||
| console.warn('oops, something went wrong.'); | |||
| file.cancel(); | |||
| }; | |||
| let loadNext = () => { | |||
| let start = currentChunk * chunkSize, | |||
| end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize; | |||
| fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end)); | |||
| }; | |||
| loadNext(); | |||
| }, | |||
| // 文件进度的回调 | |||
| onFileProgress(rootFile, file, chunk) { | |||
| console.log(`上传中 ${file.name},chunk:${chunk.startByte / 1024 / 1024} ~ ${chunk.endByte / 1024 / 1024}`) | |||
| }, | |||
| onFileSuccess(rootFile, file, response, chunk) { | |||
| let resp = JSON.parse(response); | |||
| if (resp.code === 0 && resp.merge === false) { | |||
| console.log('上传成功,不需要合并'); | |||
| } else { | |||
| axios.post('http://localhost:9999/up.php?action=merge', { | |||
| filename: file.name, | |||
| identifier: file.uniqueIdentifier, | |||
| totalSize: file.size, | |||
| totalChunks: chunk.offset + 1 | |||
| }).then(function(res){ | |||
| if (res.code === 0) { | |||
| console.log('上传成功') | |||
| } else { | |||
| console.log(res.message); | |||
| } | |||
| }) | |||
| .catch(function(error){ | |||
| console.log(error); | |||
| }); | |||
| } | |||
| }, | |||
| onFileError(rootFile, file, response, chunk) { | |||
| console.log('Error:', response) | |||
| }, | |||
| } | |||
| } | |||
| </script> | |||
| <style scoped lang="less"> | |||
| .main{ | |||
| max-width: 1000px; | |||
| margin: 10px auto; | |||
| background: #fff; | |||
| padding: 10px; | |||
| h2{ | |||
| padding: 30px 0; | |||
| text-align: center; | |||
| font-size: 20px; | |||
| } | |||
| } | |||
| .uploader { | |||
| width: 880px; | |||
| padding: 15px; | |||
| margin: 20px auto 0; | |||
| font-size: 14px; | |||
| box-shadow: 0 0 10px rgba(0, 0, 0, .4); | |||
| .uploader-btn { | |||
| margin-right: 4px; | |||
| color: #fff; | |||
| padding: 6px 16px; | |||
| } | |||
| .upfile{ | |||
| border: 1px solid #409eff; | |||
| background: #409eff; | |||
| } | |||
| .updir{ | |||
| border: 1px solid #67c23a; | |||
| background: #67c23a; | |||
| } | |||
| .uploader-list { | |||
| max-height: 440px; | |||
| overflow: auto; | |||
| overflow-x: hidden; | |||
| overflow-y: auto; | |||
| height: 356px; | |||
| /deep/.iconfont { | |||
| font-size: 18px; | |||
| color: #409eff; | |||
| } | |||
| .no-file { | |||
| text-align: center; | |||
| font-size: 14px; | |||
| padding-top: 50px; | |||
| color: #ccc; | |||
| } | |||
| } | |||
| } | |||
| //手机等小屏幕手持设备。当设备宽度 在 320px和768px之间时,执行当前的css | |||
| @media only screen and (min-width: 320px) and (max-width: 768px) { | |||
| .uploader { | |||
| width: 98%; | |||
| padding: 0; | |||
| box-shadow: none; | |||
| } | |||
| } | |||
| </style> | |||
| @@ -1,233 +0,0 @@ | |||
| <template> | |||
| <div id="page"> | |||
| <header> | |||
| <h1 class="logo"><a href="http://www.helloweba.net" title="返回helloweba首页">helloweba</a></h1> | |||
| </header> | |||
| <div class="main"> | |||
| <h2><a href="http://www.helloweba.net/javascript/636.html">文件上传之秒传文件</a></h2> | |||
| <uploader | |||
| ref="uploader" | |||
| :options="options" | |||
| :file-status-text="fileStatusText" | |||
| :autoStart="false" | |||
| @file-added="onFileAdded" | |||
| @file-progress="onFileProgress" | |||
| @file-success="onFileSuccess" | |||
| @file-error="onFileError" | |||
| class="uploader"> | |||
| <uploader-unsupport></uploader-unsupport> | |||
| <uploader-drop> | |||
| <uploader-btn class="upfile"><i class="iconfont icon-upload"></i> 上传文件</uploader-btn> | |||
| <uploader-btn class="updir" :directory="true"><i class="iconfont icon-dir"></i> 上传文件夹</uploader-btn> | |||
| </uploader-drop> | |||
| <uploader-list></uploader-list> | |||
| </uploader> | |||
| </div> | |||
| <footer> | |||
| <p>Powered by helloweba.net 允许转载、修改和使用本站的DEMO,但请注明出处:<a href="http://www.helloweba.net">www.helloweba.net</a></p> | |||
| <p class="hidden"></p> | |||
| </footer> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| import axios from 'axios'; | |||
| import SparkMD5 from 'spark-md5'; | |||
| export default { | |||
| data () { | |||
| return { | |||
| options: { | |||
| target: 'http://localhost:9999/up.php', | |||
| chunkSize: 2097152, //2MB | |||
| simultaneousUploads: 1, //并发上传数 | |||
| headers: { | |||
| 'X-token': 'abcd123' | |||
| }, | |||
| maxChunkRetries: 2, //最大自动失败重试上传次数 | |||
| parseTimeRemaining: (timeRemaining, parsedTimeRemaining) => { //格式化时间 | |||
| return parsedTimeRemaining | |||
| .replace(/\syears?/, '年') | |||
| .replace(/\days?/, '天') | |||
| .replace(/\shours?/, '小时') | |||
| .replace(/\sminutes?/, '分钟') | |||
| .replace(/\sseconds?/, '秒') | |||
| }, | |||
| testChunks: true, //开启服务端分片校验 | |||
| // 服务器分片校验函数 | |||
| checkChunkUploadedByResponse: (chunk, message) => { | |||
| let obj = JSON.parse(message); | |||
| if (obj.isExist) { | |||
| this.statusTextMap.success = '秒传文件'; | |||
| return true; | |||
| } | |||
| return (obj.uploaded || []).indexOf(chunk.offset + 1) >= 0 | |||
| }, | |||
| }, | |||
| statusTextMap: { | |||
| success: '上传成功', | |||
| error: '上传出错了', | |||
| uploading: '上传中...', | |||
| paused: '暂停', | |||
| waiting: '等待中...', | |||
| cmd5: '计算md5...' | |||
| }, | |||
| fileStatusText: (status, response) => { | |||
| return this.statusTextMap[status]; | |||
| }, | |||
| } | |||
| }, | |||
| mounted() { | |||
| }, | |||
| created() { | |||
| // | |||
| }, | |||
| methods: { | |||
| onFileAdded(file) { | |||
| // 计算MD5 | |||
| this.computeMD5(file); | |||
| }, | |||
| //计算MD5 | |||
| computeMD5(file) { | |||
| let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice, | |||
| chunkSize = 2097152, | |||
| chunks = Math.ceil(file.size / chunkSize), | |||
| currentChunk = 0, | |||
| spark = new SparkMD5.ArrayBuffer(), | |||
| fileReader = new FileReader(); | |||
| let time = new Date().getTime(); | |||
| file.cmd5 = true; | |||
| fileReader.onload = (e) => { | |||
| spark.append(e.target.result); // Append array buffer | |||
| currentChunk++; | |||
| if (currentChunk < chunks) { | |||
| //console.log(`第${currentChunk}分片解析完成, 开始第${currentChunk +1} / ${chunks}分片解析`); | |||
| let percent = Math.floor(currentChunk / chunks * 100); | |||
| file.cmd5progress = percent; | |||
| loadNext(); | |||
| } else { | |||
| console.log('finished loading'); | |||
| let md5 = spark.end(); | |||
| console.log(`MD5计算完成:${file.name} \nMD5:${md5} \n分片:${chunks} 大小:${file.size} 用时:${new Date().getTime() - time} ms`); | |||
| spark.destroy(); //释放缓存 | |||
| file.uniqueIdentifier = md5; //将文件md5赋值给文件唯一标识 | |||
| file.cmd5 = false; //取消计算md5状态 | |||
| file.resume(); //开始上传 | |||
| } | |||
| }; | |||
| fileReader.onerror = () => { | |||
| console.warn('oops, something went wrong.'); | |||
| file.cancel(); | |||
| }; | |||
| let loadNext = () => { | |||
| let start = currentChunk * chunkSize, | |||
| end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize; | |||
| fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end)); | |||
| }; | |||
| loadNext(); | |||
| }, | |||
| // 文件进度的回调 | |||
| onFileProgress(rootFile, file, chunk) { | |||
| console.log(`上传中 ${file.name},chunk:${chunk.startByte / 1024 / 1024} ~ ${chunk.endByte / 1024 / 1024}`) | |||
| }, | |||
| onFileSuccess(rootFile, file, response, chunk) { | |||
| let resp = JSON.parse(response); | |||
| //合并分片 | |||
| if (resp.code === 0 && resp.merge === true) { | |||
| axios.post('http://localhost:9999/up.php?action=merge', { | |||
| filename: file.name, | |||
| identifier: file.uniqueIdentifier, | |||
| totalSize: file.size, | |||
| totalChunks: chunk.offset + 1 | |||
| }).then(res => { | |||
| if (res.data.code === 0) { | |||
| console.log('上传成功') | |||
| } else { | |||
| console.log(res.data.message); | |||
| } | |||
| }) | |||
| .catch(error => { | |||
| console.log(error); | |||
| }); | |||
| } | |||
| }, | |||
| onFileError(rootFile, file, response, chunk) { | |||
| console.log('Error:', response) | |||
| }, | |||
| } | |||
| } | |||
| </script> | |||
| <style scoped lang="less"> | |||
| .main{ | |||
| max-width: 1000px; | |||
| margin: 10px auto; | |||
| background: #fff; | |||
| padding: 10px; | |||
| h2{ | |||
| padding: 30px 0; | |||
| text-align: center; | |||
| font-size: 20px; | |||
| } | |||
| } | |||
| .uploader { | |||
| width: 880px; | |||
| padding: 15px; | |||
| margin: 20px auto 0; | |||
| font-size: 14px; | |||
| box-shadow: 0 0 10px rgba(0, 0, 0, .4); | |||
| .uploader-btn { | |||
| margin-right: 4px; | |||
| color: #fff; | |||
| padding: 6px 16px; | |||
| } | |||
| .upfile{ | |||
| border: 1px solid #409eff; | |||
| background: #409eff; | |||
| } | |||
| .updir{ | |||
| border: 1px solid #67c23a; | |||
| background: #67c23a; | |||
| } | |||
| .uploader-list { | |||
| max-height: 440px; | |||
| overflow: auto; | |||
| overflow-x: hidden; | |||
| overflow-y: auto; | |||
| height: 356px; | |||
| /deep/.iconfont { | |||
| font-size: 18px; | |||
| color: #409eff; | |||
| } | |||
| } | |||
| } | |||
| //手机等小屏幕手持设备。当设备宽度 在 320px和768px之间时,执行当前的css | |||
| @media only screen and (min-width: 320px) and (max-width: 768px) { | |||
| .uploader { | |||
| width: 98%; | |||
| padding: 0; | |||
| box-shadow: none; | |||
| } | |||
| } | |||
| </style> | |||
| @@ -1,112 +0,0 @@ | |||
| <template> | |||
| <div id="page"> | |||
| <header> | |||
| <h1 class="logo"><a href="http://www.helloweba.net" title="返回helloweba首页">helloweba</a></h1> | |||
| </header> | |||
| <div class="main"> | |||
| <h2><a href="http://www.helloweba.net/javascript/632.html">使用vue-simple-uploader上传文件和文件夹</a></h2> | |||
| <p style="text-align:center;color: #999">温馨提示:可将文件拖动到灰色区域上传</p> | |||
| <uploader :options="options" :fileStatusText="fileStatusText" class="uploader"> | |||
| <uploader-unsupport></uploader-unsupport> | |||
| <uploader-drop> | |||
| <uploader-btn class="upfile"><i class="iconfont icon-upload"></i> 上传文件</uploader-btn> | |||
| <uploader-btn class="updir" :directory="true"><i class="iconfont icon-dir"></i> 上传文件夹</uploader-btn> | |||
| </uploader-drop> | |||
| <uploader-list></uploader-list> | |||
| </uploader> | |||
| </div> | |||
| <footer> | |||
| <p>Powered by helloweba.net 允许转载、修改和使用本站的DEMO,但请注明出处:<a href="http://www.helloweba.net">www.helloweba.net</a></p> | |||
| <p class="hidden"></p> | |||
| </footer> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| export default { | |||
| data () { | |||
| return { | |||
| options: { | |||
| target: 'uploader.php', | |||
| testChunks: false, | |||
| chunkSize: 1024*1024*128, //128MB | |||
| simultaneousUploads: 1, //并发上传数 | |||
| headers: { | |||
| 'X-token': 'abcd123' | |||
| }, | |||
| maxChunkRetries: 3, //最大自动失败重试上传次数 | |||
| }, | |||
| fileStatusText: { | |||
| success: '上传成功', | |||
| error: '上传出错了', | |||
| uploading: '上传中...', | |||
| paused: '暂停', | |||
| waiting: '等待中...' | |||
| }, | |||
| attrs: { | |||
| accept: 'image/*' | |||
| } | |||
| } | |||
| }, | |||
| // mounted () { | |||
| // this.$nextTick(() => { | |||
| // window.uploader = this.$refs.uploader.uploader | |||
| // }) | |||
| // } | |||
| } | |||
| </script> | |||
| <style scoped lang="less"> | |||
| .main{ | |||
| max-width: 1000px; | |||
| margin: 10px auto; | |||
| background: #fff; | |||
| padding: 10px; | |||
| h2{ | |||
| padding: 30px 0; | |||
| text-align: center; | |||
| font-size: 20px; | |||
| } | |||
| } | |||
| .uploader { | |||
| width: 880px; | |||
| padding: 15px; | |||
| margin: 20px auto 0; | |||
| font-size: 14px; | |||
| box-shadow: 0 0 10px rgba(0, 0, 0, .4); | |||
| .uploader-btn { | |||
| margin-right: 4px; | |||
| color: #fff; | |||
| padding: 6px 16px; | |||
| } | |||
| .upfile{ | |||
| border: 1px solid #409eff; | |||
| background: #409eff; | |||
| } | |||
| .updir{ | |||
| border: 1px solid #67c23a; | |||
| background: #67c23a; | |||
| } | |||
| .uploader-list { | |||
| max-height: 440px; | |||
| overflow: auto; | |||
| overflow-x: hidden; | |||
| overflow-y: auto; | |||
| } | |||
| // /deep/.uploader-drop{ | |||
| // height: 400px; | |||
| // } | |||
| } | |||
| //手机等小屏幕手持设备。当设备宽度 在 320px和768px之间时,执行当前的css | |||
| @media only screen and (min-width: 320px) and (max-width: 768px) { | |||
| .uploader { | |||
| width: 98%; | |||
| padding: 0; | |||
| box-shadow: none; | |||
| } | |||
| } | |||
| </style> | |||
| @@ -22,15 +22,10 @@ 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 router from './router' | |||
| import uploader from 'vue-simple-uploader' | |||
| Vue.use(uploader); | |||
| //import './assets/css/iconfont.css' | |||
| //Vue.config.productionTip = false | |||
| const {AppSubUrl, StaticUrlPrefix, csrf} = window.config; | |||
| @@ -3180,7 +3175,6 @@ function initVueUploader() { | |||
| /* eslint-disable no-new */ | |||
| new Vue({ | |||
| el: '#uploader', | |||
| router, | |||
| components: { App }, | |||
| template: '<App/>' | |||
| }); | |||
| @@ -1,39 +0,0 @@ | |||
| import Vue from 'vue' | |||
| import Router from 'vue-router' | |||
| import Uploader from '../components/Uploader.vue' | |||
| import Chunk from '../components/Chunk.vue' | |||
| import Md5 from '../components/Md5.vue' | |||
| import Skip from '../components/Skip.vue' | |||
| import Breakpoint from '../components/Breakpoint.vue' | |||
| Vue.use(Router) | |||
| export default new Router({ | |||
| routes: [ | |||
| { | |||
| path: '/', | |||
| name: 'uploader', | |||
| component: Uploader | |||
| }, | |||
| { | |||
| path: '/chunk', | |||
| name: 'chunk', | |||
| component: Chunk | |||
| }, | |||
| { | |||
| path: '/skip', | |||
| name: 'skip', | |||
| component: Skip | |||
| }, | |||
| { | |||
| path: '/md5', | |||
| name: 'md5', | |||
| component: Md5 | |||
| }, | |||
| { | |||
| path: '/breakpoint', | |||
| name: 'breakpoint', | |||
| component: Breakpoint | |||
| }, | |||
| ] | |||
| }) | |||