获取中...

-

Just a minute...

图片上传

简单的图片上传

前端

1
2
<input type="file" id="file">

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function upload() {
var formData = new FormData();
var fileField = document.querySelector("#file");
const file = fileField.files[0];
formData.append('inputFile', file);
fetch('/upload', {
method: 'POST',
body: formData,
})
.then(res => res.json())
.then(res => {
document.querySelector("#url").value = res.url
});
}

服务端使用express的一个插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const upload = require('multer')();
const router = require('express').Router();

router.post('/upload', upload.fields([{ name: 'inputFile' }]), async (req, res) => {
//文件
const file = req.files['inputFile'][0];
const md5Name = crypto
.createHash('md5')
.update(file.buffer, 'utf-8')
.digest('hex');//计算出上传文件的md5,当文件名
const extname = path.extname(file.originalname)//后缀名
// 存储到cos的路径
const Key = '/upload/' + md5Name + extnam

try {
// 利用自己封装的腾讯oss 上传模块
const { url } = await oss.upload({
Key: Key,
Body: file.buffer
});
res.send({
code: 200,
data: file.originalname,
url
})

} catch (error) {
res.send({ code: 500, msg: error })
}
});

最后部署到腾讯云 serveless上,没啥问题直到上传的文件超过6M
查阅文档,提交工单,得到的说法都是网关会拦超过6M的请求体

没办法,分片上传

分片上传

思路

  1. 前端切片,按顺序打上标签,然后分别上传到后端,后端存储到临时文件夹
  2. 前端发送上传完成的请求,后端将切片的文件合并
  3. 删除临时文件

前端

html没有区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
const bytesPerPiece = 4 * 1024 * 1024; // 每个文件切片大小定为5MB .

function upload() {
var formData = new FormData();
var fileField = document.querySelector("#file");
const file = fileField.files[0];
const { size } = file;
// 大于5M,分片上传
if (size > bytesPerPiece) {
chunkUpload(file)
} else {

formData.append('inputFile', file);

fetch('/upload', {
method: 'POST',
body: formData,
})
.then(res => res.json())
.then(res => {
document.querySelector("#url").value = res.url
});
}
}
// 切割并分片上传
async function chunkUpload(file) {
const { size, name, type } = file;
// 分片上传
let index = 0;
const totalPieces = Math.ceil(size / bytesPerPiece);
console.time('上传完成')

while (index < totalPieces) {
const start = index * bytesPerPiece;
let end = start + bytesPerPiece;
if (end > size) {
end = size
}
const chunk = file.slice(start, end);//切割文件
const sliceIndex = `${name}.${index}`;
const formData = new FormData();

formData.append("chunk", chunk)
formData.append("fileName", name)
formData.append("chunkName", sliceIndex);

console.log(formData)

await fetch('/uploadChunk', {
method: 'POST',
body: formData,
});
index++
}

console.timeEnd('上传完成')
console.log('上传完成')

fetch('/joinChunk', {
method: 'post',
headers: {
'Content-Type': "application/json;charset=UTF-8"
},
body: JSON.stringify({ 'fileName': name })
})
.then(res => res.json())
.then(res => {
document.querySelector("#url").value = res.url
})

}

后端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
router.post('/uploadChunk', upload.fields([{ name: 'chunk' }]), async (req, res) => {
const file = req.files['chunk'][0];//文件名
const buffer = file.buffer;
const { chunkName, fileName } = req.body;

const chunkPath = join(__dirname, `../temp/chunk/${fileName}`, chunkName)
const filePath = join(__dirname, `../temp/chunk/${fileName}`)

if (!fs.existsSync(filePath)) {
fs.mkdirSync(filePath);
}

fs.writeFileSync(chunkPath, buffer);

res.send({
code: 200,
data: filePath,
msg: `${chunkName}上传成功`
})
});

router.post('/joinChunk', async (req, res) => {
const { fileName } = req.body;
console.log(req.body)
const dir = join(__dirname, `../temp/chunk/${fileName}`)
const file = join(__dirname, `../temp/file/${fileName}`)
const files = fs.readdirSync(dir);

files
.sort((a, b) => {
const aNum = Number(a.split('.').pop())
const bNum = Number(b.split('.').pop());
return aNum > bNum ? 1 : -1
});

files.forEach(chunkName => {
const chunkUrl = join(dir, chunkName)
const buffer = fs.readFileSync(chunkUrl);

fs.appendFileSync(file, buffer)
fs.unlinkSync(chunkUrl)

});

fs.rmdirSync(dir);
const fullFile = fs.readFileSync(file)

const md5Name = crypto.createHash('md5').update(fullFile, 'utf-8').digest('hex');//计算出上传文件的md5,当做文件名
const extname = path.extname(file);//后缀名
const Key = '/upload/' + md5Name + extname// 存储到cos的路径
console.log(Key)
try {
const { url } = await oss.upload({
Key: Key,
Body: fullFile
});
res.send({
code: 200,
data: fileName,
url
})

} catch (error) {
res.send({ code: 500, msg: error })
} finally {
fs.unlinkSync(file)
}
})

本地测试毫无问题,上传到serveless,结果您猜怎么着?

反复研究了文档,烦了n多次客服之后,确定网关的6MB大小小智是base64之后要小于这个值
二进制传输的Binary和content-length不是,base64之后的大小菜市
得出以下几种解决方案

  1. 将碎片文件直接存到cos里面,再读取cos里面的碎片合并到cos
    优点:cos和scf在同一区域,传输流量免费
    缺点:碎片受到网关请求体大小限制

  2. 利用sfc的临时文件目录可以读写,将碎片合并到临时目录,上传到cos
    有点:只传输一次到cos
    缺点:碎片受到网关请求体大小限制

  3. 前端直接用cdk传到cos
    优点:不走网关,没有大小限制
    缺点:md5、鉴权都要放到前端

综合考虑,准备放弃走网关了,直接前端上传

api服务就是应该提供一些鉴权,功能

相关文章
评论
分享
  • 利用微信小程序扫码授权

    微信小程序扫码授权背景想要使用微信扫码登录自己的网址,通过授权快速获取用户的昵称,头像功能由于没有企业认证账号,故只能通过微信小程序实现, 体验地址https://api.nnnnzs.cn/screen-demo.html?env=...

    利用微信小程序扫码授权
  • 强制加载element-dialog

    强制加载element-dialog背景123<el-dialog> <MyComponent /></el-dialog> 自己封装的组件 MyComponent ,放在了el-dialog里...

    强制加载element-dialog
  • leancloud-基础存储操作

    对象安装使用1234npm install leancloud-storage --save# debug模式DEBUG=leancloud* node src/leancloud.js 初始化12345678const AV = ...

    leancloud-基础存储操作
  • leetcode-540-有序数组中的单一元素

    给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。请你找出并返回只出现一次的那个数。你设计的解决方案必须满足 O(log n) 时间复杂度和 O(1) 空间复杂度。 示例 1:输入: nums = [1...

    leetcode-540-有序数组中的单一元素
  • 1995.统计特殊四元组

    1995. 统计特殊四元组给你一个 下标从 0 开始 的整数数组 nums ,返回满足下述条件的 不同 四元组 (a, b, c, d) 的 数目 :nums[a] + nums[b] + nums[c] == nums[d] ,且a...

    1995.统计特殊四元组
  • leetcode-1078.Bigram 分词

    给出第一个词 first 和第二个词 second,考虑在某些文本 text 中可能以 “first second third” 形式出现的情况,其中 second 紧随 first 出现,third 紧随 second 出现。对于每...

    leetcode-1078.Bigram 分词
  • leetcode-1705.吃苹果的最大数目

    有一棵特殊的苹果树,一连 n 天,每天都可以长出若干个苹果。在第 i 天,树上会长出 apples[i] 个苹果,这些苹果将会在 days[i] 天后(也就是说,第 i + days[i] 天时)腐烂,变得无法食用。也可能有那么几天,...

    leetcode-1705.吃苹果的最大数目
  • leetcode-1154.一年中的第几天

    1154. 一年中的第几天给你一个字符串 date ,按 YYYY-MM-DD 格式表示一个 现行公元纪年法 日期。请你计算并返回该日期是当年的第几天。通常情况下,我们认为 1 月 1 日是每年的第 1 天,1 月 2 日是每年的第 ...

    leetcode-1154.一年中的第几天
  • leetcode-475.供暖器

    冬季已经来临。 你的任务是设计一个有固定加热半径的供暖器向所有房屋供暖。在加热器的加热半径范围内的每个房屋都可以获得供暖。现在,给出位于一条水平线上的房屋 houses 和供暖器 heaters 的位置,请你找出并返回可以覆盖所有房屋...

    leetcode-475.供暖器
  • leetcode-997.找到小镇的法官

    997. 找到小镇的法官在一个小镇里,按从 1 到 n 为 n 个人进行编号。传言称,这些人中有一个是小镇上的秘密法官。如果小镇的法官真的存在,那么:小镇的法官不相信任何人。每个人(除了小镇法官外)都信任小镇的法官。只有一个人同时满足...

    leetcode-997.找到小镇的法官