blog/content/post/奇技淫巧/签名URL注意.md
2025-02-21 22:15:47 +08:00

2.6 KiB
Raw Blame History

title description date slug categories tags lastmod
使用签名URL上传文件的一个注意事项 使用签名URL上传文件时需要特别小心处理请求的Content-Type头 2025-02-21T21:59:00+08:00 signurl
奇技淫巧
前端
后端
JavaScript
Node.js
OSS
建站
签名URL
2025-02-21T22:13:00+08:00

如果后端用阿里云OSS SDK生成了签名URL前端直接使用签名URL上传文件时为了避免把文件传给后端服务器占据内存常见的实践是前端把文件类型传给后端后端根据文件类型生成带Content-Type头的签名URL前端直接使用签名URL上传文件。

这里有一个问题如果我们不手动指定后端签名使用的Content-Type头那么后端签名时会认为请求不具有Content-Type头。但是前端在使用签名URL进行上传时浏览器会自动设置Content-Type头为指定的文件类型就会导致OSS服务器校验签名URL失败返回403错误。

所以,后端在生成签名URL时必须手动指定Content-Type头。最佳的实践是前端把文件的MIME类型传给后端后端根据MIME类型生成带Content-Type头的签名URL。

后端:

const oss = require('ali-oss')
const express = require('express')
const app = express()
const client = new oss({
    accessKeyId: 'yourAccessKeyId',
    // ...
    authorizationV4: true // 使用V4签名
})

app.use(express.json())

app.post('/get-signed-url', (req, res) => {
    const { type } = req.body
    const url = client.signatureUrlV4(
        'PUT',
        20, // 有效期20秒
        {
            headers: {
                'Content-Type': type  // 签名中指定Content-Type头
            }
        }
    )
    res.json({ url })
})

前端:

const file = document.querySelector('input[type="file"]').files[0]
const type = file.type || 'application/octet-stream'  // 获取文件的MIME类型
const url = await fetch('/get-signed-url', {
    method: 'POST',
    body: JSON.stringify({ type })
}).then(res => res.json())

const upload = await fetch(url, {
    method: 'PUT',
    body: file,
    headers: {
        'Content-Type': type  // 必须保证跟签名的Content-Type头一致
    }
})

这样就能保证上传的文件类型与签名URL的Content-Type头一致不会出现403错误。

另外我发现在签名URL中附带Authorization头是无效操作猜测Authorization头会被OSS服务器忽略。后端即使在签名时指定了Authorization头前端不携带Authorization头也能上传成功。