在七牛云建立并配置好空间后,在 ECS 做如下设置

  1. 1Panel 创建静态网站,配置 DNS 解析,配置 SSL 证书

  2. 配置前端页面

  3. 配置后端服务

  4. 1Panel 配置反向代理

前端页面 photos.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>照片上传</title>
    <style>
        body { font-family: Arial, sans-serif; max-width: 500px; margin: 0 auto; padding: 20px; }
        button { background: #1890ff; color: white; border: none; padding: 10px 15px; border-radius: 4px; }
        #preview { margin-top: 20px; }
        img { max-width: 100px; margin: 5px; }
    </style>
</head>
<body>
    <h2>📸 请上传照片</h2>
    <input type="file" id="fileInput" multiple accept="image/*">
    <button onclick="upload()">一键上传</button>
    <div id="preview"></div>

    <script src="https://unpkg.com/qiniu-js@2.5.5/dist/qiniu.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
    <script>
        // 配置区
        const bucket = "空间名";
        const prefix = "photos"; // 空间下建立好 photos 目录
        const domain = "空间域名";

        // ✅ 从后端获取 Token 的异步函数
        async function getUploadToken() {
            try {
                const response = await fetch('/qiniu/token_for_photos');
                if (!response.ok) throw new Error('获取 Token For Photos 失败');
                const { token } = await response.json();
                return token;
            } catch (err) {
                alert('无法获取上传凭证,请稍后重试');
                throw err;
            }
        }

        // 显示预览
        document.getElementById('fileInput').addEventListener('change', function(e) {
            const preview = document.getElementById('preview');
            preview.innerHTML = '';
            Array.from(e.target.files).forEach(file => {
                const img = document.createElement('img');
                img.src = URL.createObjectURL(file);
                preview.appendChild(img);
            });
        });

        // 上传函数,使用后端 Token
        async function upload() {
            const files = document.getElementById('fileInput').files;
            if (files.length === 0) return alert('请先选择照片!');

            try {
                // 从后端获取 Token
                const uploadToken = await getUploadToken();
                console.log("后端返回的Token:", uploadToken);

                Array.from(files).forEach(file => {
                    const key = prefix + Date.now() + "-" + file.name;
                    const observable = qiniu.upload(file, key, uploadToken);

                    observable.subscribe({
                        complete: (res) => {
                            alert(`上传成功!链接:https://${domain}/${res.key}`);
                        },
                        error: (err) => {
                            console.error(err);
                            alert('上传失败:' + err.message);
                        }
                    });
                });
            } catch (err) {
                console.error("上传流程错误:", err);
            }
        }
    </script>
</body>
</html>

后端服务 ****/services/photos.js

// photos.js
const express = require('express');
const qiniu = require('qiniu');
const app = express();
const port = process.env.PHOTOS_PORT || 3001;

// 加载环境变量
require('dotenv').config();

// 七牛云认证
const mac = new qiniu.auth.digest.Mac(
  process.env.QINIU_ACCESS_KEY,
  process.env.QINIU_SECRET_KEY
);

// 生成上传凭证
    scope: process.env.QINIU_BUCKET,
    deadline: Math.floor(Date.now() / 1000) + 3600,
    returnBody: '{"key":"$(key)"}'
  };

app.get('/qiniu/token_for_photos', (req, res) => {
  const options = {
    scope: process.env.QINIU_BUCKET,
    // 凭证有效期:1 小时
    deadline: Math.floor(Date.now() / 1000) + 3600,
    // 禁止覆盖已有文件
    insertOnly: 1,
    // 限制文件类型
    mimeLimit: "image/*",
    // 限制文件大小:最大 20 MB
    fsizeLimit: 20 * 1024 * 1024,
    // 强制返回上传后的文件路径
    returnBody: '{"key":"$(key)"}'
  };

  const putPolicy = new qiniu.rs.PutPolicy(options);
  const uploadToken = putPolicy.uploadToken(mac);

  res.json({ token: uploadToken });
});

// 启动服务
app.listen(port, () => {
  console.log(`Token 服务运行在端口 ${port}`);
});

后端服务 ****/services/.env

QINIU_ACCESS_KEY=七牛用户AK
QINIU_SECRET_KEY=七牛用户SK
QINIU_BUCKET=空间名
QINIU_DOMAIN=空间域名
PHOTOS_PORT=3001

后端服务 /etc/systemd/system/qiniu-upload-photos.service

[Unit]
Description=Qiniu Upload Photos Service
After=network.target

[Service]
User=root
WorkingDirectory=****/services
ExecStart=/usr/bin/node photos.js
Restart=always
EnvironmentFile=****/services/.env

[Install]
WantedBy=multi-user.target

运行服务:

systemctl daemon-reload
systemctl start qiniu-upload-photos
systemctl enable qiniu-upload-photos

网站中配置反代:

1panel-七牛-上传-反代.png

之后在源文中修改,以 https://aaa.bbb.ccc 为例:

location ^~ /qiniu/token_for_photos {

    # 1. 校验 Origin(来源域名)
    if ($http_origin !~* ^(https?://aaa\.bbb\.ccc)?$) {
        return 403;
    }
    # 2. 校验 Referer(来源页面)
    if ($http_referer !~* ^https?://aaa\.bbb\.ccc/.*$) {
        return 403;
    }

    proxy_pass http://127.0.0.1:3001; 
    ....
}