文章

七牛云上传文件

七牛云上传文件

七牛云上传文件

七牛云上传文件

客户端:react: ^17.0.2,antd: “^4.20.1

服务端:egg: ^2.15.1,qiniu: ^7.8.0”�

客户端

1:获取上传凭证�(token)

2:文件上传

  1. 直接上传七牛云
  2. 通过服务端封装的上传接口上传(可通过 antdcustomRequest 自定义上传)

这里我用第一种方式:通过 antdUpload 组件上传,这里需要注意 存储区域

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
const UploadIProps: UploadProps = {
  name: 'file',
  fileList: [],
  action: 'http://up-cn-east-2.qiniup.com',
  data: {
    key: filename,
    token: token
  },
  beforeUpload: async (file) => {
    await getUploadToken(file); // 这里是从服务端拿上传凭证
  },
  onChange: async (info) => {
    if (info.file.status === 'done') {
      setPrivateUrl([]);
      await getFileList();
      message.success('uploaded successfully');
    } else if (info.file.status === 'error') {
      message.error('upload failed');
    }
  }
};

const App: React.FC = () => (
  <Upload {...UploadIProps}>
    <Button icon={<UploadOutlined />}>Click to Upload</Button>
  </Upload>
);

注意!!! 上传的时候,如果遇到类似400:incorrect region, please use xxx.qiniu.com的错误,说明是上传域名和空间所在的区域不匹配,多发生在旧的sdk、工具和插件上。后面的 xxx.qiniu.com是正确的上传域名。 存储区域和上传域名的对应关系见https://developer.qiniu.com/kodo/manual/1671/region-endpoint

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 获取上传token
async getToken() {
  const { ctx, app: { config: { qiNiu: { bucket, accessKey, secretKey } } } } = this;
  try {
    const mac = new QN.auth.digest.Mac(accessKey, secretKey);
    const options = {
      scope: bucket,
      expires: 60 * 60,
      fsizeLimit: 1024 * 1024, // 限定上传文件大小最大值,单位Byte(这里1MB)
      mimeLimit: 'image/*', // 限定用户上传的文件类型
    };
    const putPolicy = new QN.rs.PutPolicy(options);
    const uploadToken = putPolicy.uploadToken(mac);
    ctx.Success('添加成功', uploadToken);
  } catch (e) {
    ctx.Fail('获取token失败');
  }
}

扩展

获取指定前缀的文件列表 - 私有空间

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
// 获取指定前缀的文件列表 - 私有空间
async getFileList() {
  const { ctx } = this;
  const { data, msg, success } = await this.getPrefixList();
  if (!success) {
    return ctx.Fail(msg);
  }
  ctx.Success('success', data);
}

getPrefixList() {
  return new Promise(resolve => {
    const { ctx, app: { config: { qiNiu: { bucket, accessKey, secretKey } } } } = this;
    const { limit, prefix, marker } = ctx.params;
    const mac = new QN.auth.digest.Mac(accessKey, secretKey);
    const config = new QN.conf.Config();
    // config.useHttpsDomain = true;
    config.zone = QN.zone.Zone_z0;
    const bucketManager = new QN.rs.BucketManager(mac, config);
    const privateBucketDomain = 'http://rs6aoe3h8.bkt.clouddn.com';
    const deadline = parseInt(Date.now() / 1000) + 3600; // 1小时过期
    const options = {
      limit,
      marker,
      prefix,
    };

    try {
      bucketManager.listPrefix(bucket, options, (err, respBody, respInfo) => {
        if (err) {
          throw err;
        }
        if (respInfo.statusCode === 200) {
          // 如果这个nextMarker不为空,那么还有未列举完毕的文件列表,下次调用listPrefix的时候,
          // 指定options里面的marker为这个值
          const nextMarker = respBody.marker;
          const items = respBody.items;
          const obj = {
            nextMarker: nextMarker ? nextMarker : '',
            privateDownloadUrl: [],
          };
          items.forEach(function(item) {
            const privateDownloadUrl = bucketManager.privateDownloadUrl(privateBucketDomain, item.key, deadline);
            obj.privateDownloadUrl.push(privateDownloadUrl);
          });
          resolve({ data: obj, success: true });
        } else {
          resolve({ msg: respInfo, success: false });
        }
      });
    } catch (e) {
      resolve({ msg: e, success: false });
    }
  });
}
本文由作者按照 CC BY 4.0 进行授权