Browse Source

chunk url

tags/v1.21.12.1
yuyuanshifu 5 years ago
parent
commit
dbbec302c7
22 changed files with 144 additions and 1259 deletions
  1. +2
    -2
      modules/minio_ext/api.go
  2. +1
    -1
      modules/storage/minio_ext.go
  3. +10
    -5
      routers/repo/attachment.go
  4. +131
    -13
      web_src/js/App.vue
  5. BIN
      web_src/js/assets/bg.png
  6. BIN
      web_src/js/assets/body_bg.gif
  7. +0
    -65
      web_src/js/assets/css/iconfont.css
  8. BIN
      web_src/js/assets/css/iconfont.eot
  9. +0
    -1
      web_src/js/assets/css/iconfont.js
  10. +0
    -93
      web_src/js/assets/css/iconfont.json
  11. +0
    -62
      web_src/js/assets/css/iconfont.svg
  12. BIN
      web_src/js/assets/css/iconfont.ttf
  13. BIN
      web_src/js/assets/css/iconfont.woff
  14. BIN
      web_src/js/assets/css/iconfont.woff2
  15. BIN
      web_src/js/assets/logo.png
  16. +0
    -230
      web_src/js/components/Breakpoint.vue
  17. +0
    -153
      web_src/js/components/Chunk.vue
  18. +0
    -244
      web_src/js/components/Md5.vue
  19. +0
    -233
      web_src/js/components/Skip.vue
  20. +0
    -112
      web_src/js/components/Uploader.vue
  21. +0
    -6
      web_src/js/index.js
  22. +0
    -39
      web_src/js/router/index.js

+ 2
- 2
modules/minio_ext/api.go View File

@@ -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)


+ 1
- 1
modules/storage/minio_ext.go View File

@@ -17,7 +17,7 @@ import (
)

const (
PresignedUploadPartUrlExpireTime = int64(time.Hour * 24 * 7)
PresignedUploadPartUrlExpireTime = time.Hour * 24 * 7
)

type ComplPart struct {


+ 10
- 5
routers/repo/attachment.go View File

@@ -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))


+ 131
- 13
web_src/js/App.vue View File

@@ -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) {


BIN
web_src/js/assets/bg.png View File

Before After
Width: 150  |  Height: 150  |  Size: 1.7 kB

BIN
web_src/js/assets/body_bg.gif View File

Before After
Width: 1  |  Height: 100  |  Size: 49 B

+ 0
- 65
web_src/js/assets/css/iconfont.css View File

@@ -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";
}


BIN
web_src/js/assets/css/iconfont.eot View File


+ 0
- 1
web_src/js/assets/css/iconfont.js
File diff suppressed because it is too large
View File


+ 0
- 93
web_src/js/assets/css/iconfont.json View File

@@ -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
}
]
}

+ 0
- 62
web_src/js/assets/css/iconfont.svg View File

@@ -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="&#58918;" 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="&#58899;" 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="&#58949;" 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="&#58966;" 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="&#58903;" 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="&#59172;" 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="&#59233;" 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="&#58911;" 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="&#58887;" 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="&#58900;" 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="&#59232;" 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="&#59014;" 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>

BIN
web_src/js/assets/css/iconfont.ttf View File


BIN
web_src/js/assets/css/iconfont.woff View File


BIN
web_src/js/assets/css/iconfont.woff2 View File


BIN
web_src/js/assets/logo.png View File

Before After
Width: 48  |  Height: 48  |  Size: 2.7 kB

+ 0
- 230
web_src/js/components/Breakpoint.vue View File

@@ -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>

+ 0
- 153
web_src/js/components/Chunk.vue View File

@@ -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>

+ 0
- 244
web_src/js/components/Md5.vue View File

@@ -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>

+ 0
- 233
web_src/js/components/Skip.vue View File

@@ -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>

+ 0
- 112
web_src/js/components/Uploader.vue View File

@@ -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>

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

@@ -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/>'
});


+ 0
- 39
web_src/js/router/index.js View File

@@ -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
},
]
})

Loading…
Cancel
Save