前端基于阿里云oss实现文件上传

485 阅读4分钟

前言

现在的文件上传,一般都会把文件上传到oss对象存储中,很少会把资源存在我们自己的业务服务端。这么做的优势主要有以下几点:

  • 海量存储无上限:单 Bucket 支持千亿级文件存储,容量可随业务增长自动扩展,解决自建服务器的存储容量瓶颈。
  • 全球访问低延迟:结合 CDN 的节点缓存,用户无论身处全球何地,均可快速获取资源,而自建方案需部署多地机房,成本高昂且技术复杂;

对象存储

  • 我们电脑的文件存储方式一般都是 目录+文件: image.png

  • oss 的存储方式如下: image.png 采用桶的概念来进行存储,一个桶就是一个 Bucket,每个桶里面可以存储很多文件。

上传

想要把文件上传到oss,有以下几种方式:

  1. 直接通过oss控制台上传

image.png

  1. 前端代码写死sts的令牌、密钥(一般不建议这么做:令牌、密钥放在代码里面,容易被窃取)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Document</title>
  </head>
  <body>
    <input id="file" type="file" />
    <button id="upload">上传</button>
    <script src="https://gosspublic.alicdn.com/aliyun-oss-sdk-6.18.0.min.js"></script>
    <script>
      const client = new OSS({
        // yourRegion填写Bucket所在地域。以华东1(杭州)为例,yourRegion填写为oss-cn-hangzhou。
        region: "yourRegion",
        authorizationV4: true,
        // 从STS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
        accessKeyId: "yourAccessKeyId",
        accessKeySecret: "yourAccessKeySecret",
        // 从STS服务获取的安全令牌(SecurityToken)。
        stsToken: "yourSecurityToken",
        // 填写Bucket名称。
        bucket: "examplebucket",
      });

      // 从输入框获取file对象,例如<input type="file" id="file" />。
      let data;
      // 创建并填写Blob数据。
      //const data = new Blob(['Hello OSS']);
      // 创建并填写OSS Buffer内容。
      //const data = new OSS.Buffer(['Hello OSS']);

      const upload = document.getElementById("upload");

      async function putObject(data) {
        try {
          // 填写Object完整路径。Object完整路径中不能包含Bucket名称。
          // 您可以通过自定义文件名(例如exampleobject.txt)或文件完整路径(例如exampledir/exampleobject.txt)的形式实现将数据上传到当前Bucket或Bucket中的指定目录。
          // data对象可以自定义为file对象、Blob数据或者OSS Buffer。
          const options = {
            meta: { temp: "demo" },
            mime: "json",
            headers: { "Content-Type": "text/plain" },
          };
          const result = await client.put("examplefile.txt", data, options);
          console.log(result);
        } catch (e) {
          console.log(e);
        }
      }

      upload.addEventListener("click", () => {
        const data = file.files[0];
        putObject(data);
      });
    </script>
  </body>
</html>
  1. 前端请求服务端接口,服务端下发认证密钥(安全性更好,实际开发也会这么使用)

服务端下发授权签名、前端拿到签名后把文件上传到oss。

  • 创建 oss-signature-demo 目录,进入该目录
mkdir oss-demo
cd oss-demo
  • 分别创建前后端代码目录
mkdir browser
mkdir service

image.png

  • 进入 service 目录,安装依赖
cd service
npm init -y
npm install ali-oss cors express
  • 创建 src/index.js文件,编写核心逻辑代码
mkdir src
touch src/index.js
const express = require('express');
const OSS = require('ali-oss');
const cors = require('cors');
const app = express();

// 添加 CORS 中间件
app.use(cors());

const config = {
  // 填入你阿里云的accessKeyId和accessKeySecret
  accessKeyId: '',
  accessKeySecret: '',
  // 填入你阿里云的bucket
  bucket: '',
}
const client = new OSS (config);

app.get('/api/oss/signature',async (req, res) => {
try {
  const date = new Date();
  // 设置签名的有效期,单位为秒。
  date.setSeconds(date.getSeconds() + 3600);
  const policy = {
    expiration: date.toISOString(),
    conditions: [
      // 设置上传文件的大小限制。
      ["content-length-range", 0, 1048576000],
      // 限制可上传的Bucket。
      { bucket: client.options.bucket },
    ],
  };
  const formData = await client.calculatePostSignature(policy);
  const host = `http://${config.bucket}.${
    (await client.getBucketLocation()).location
  }.aliyuncs.com`.toString();
  const params = {
    policy: formData.policy,
    signature: formData.Signature,
    ossAccessKeyId: formData.OSSAccessKeyId,
    host,
  };
  res.json(params);

} catch (error) {
  console.log(error);
  res.status(500).json({ error: 'Failed to generate OSS signature' });
}
});

// 设置端口
const PORT = 3000;
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
node src/index.js

此时,服务端接口已经跑起来了。接下来编写前端上传逻辑:

  • 进入前端目录,创建index.html文件
cd ..
cd browser
mkdir index.html
  • index.html 代码
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>上传文件到OSS</title>
  </head>
  <body>
    <div class="container">
      <form>
        <div class="mb-3">
          <label for="file" class="form-label">选择文件</label>
          <input
            type="file"
            class="form-control"
            id="file"
            name="file"
            required
          />
        </div>
        <button type="submit" class="btn btn-primary">上传</button>
      </form>
    </div>
    <script type="text/javascript">
      const form = document.querySelector("form");
      const fileInput = document.querySelector("#file");
      form.addEventListener("submit", (event) => {
        event.preventDefault();
        let file = fileInput.files[0];
        let filename = fileInput.files[0].name;
        fetch("http://localhost:3000/api/oss/signature", {
          method: "GET",
          headers: {
            Accept: "application/json",
          },
        })
          .then((response) => response.json())
          .then((data) => {
            const formData = new FormData();
            formData.append("name", filename);
            formData.append("policy", data.policy);
            formData.append("OSSAccessKeyId", data.ossAccessKeyId);
            formData.append("success_action_status", "200");
            formData.append("signature", data.signature);
            formData.append("key", filename);
            // file必须为最后一个表单域,除file以外的其他表单域无顺序要求。
            formData.append("file", file);
            fetch(data.host, { method: "POST", body: formData }).then((res) => {
              console.log(res);
              alert("文件已上传");
            });
          })
          .catch((error) => {
            console.log(
              "Error occurred while getting OSS upload parameters:",
              error
            );
          });
      });
    </script>
  </body>
</html>

效果

image.png

image.png

总结

我们先简单介绍了下oss存储的优势以及特征,接着通过案例介绍了如何把文件上传到oss中,主要有3种方式:

  • 直接通过oss控制台上传
  • 前端代码写死sts的令牌、密钥(一般不建议这么做:令牌、密钥放在代码里面,容易被窃取)
  • 前端请求服务端接口,服务端下发认证密钥(安全性更好,实际开发也会这么使用)

代码已经上传到:github