Skip to content

图片上传接口


获取OSS上传凭证,用于客户端直传文件到OSS存储

调用接口

请求方式: POST(HTTPS)
请求地址: https://ms-ai.chongzhiling.com/file/upload-policy?token=[TOKEN]

返回结果:

json
{
    "code": 200,
    "message": "success",
    "data": {
        "accessKeyId": "STS.xxxxxxx",
        "accessKeySecret": "8i1isYxxxxx",
        "securityToken": "CAISxxxxxxx",
        "expiration": "2025-09-11T06:20:10Z",
        "bucket": "czl-xxx",
        "endpoint": "oss-xxxx.aliyuncs.com"
    }
}

返回参数说明:

参数类型含义
accessKeyIdstring临时访问密钥ID
accessKeySecretstring临时访问密钥
securityTokenstring安全令牌
expirationstring凭证过期时间
bucketstringOSS存储桶名称
endpointstringOSS接入点

打开直连测试页面

https://ms-ai.chongzhiling.com/test-oss-upload.html?token=TOKEN

客户端示例代码:

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>
    <script src="https://gosspublic.alicdn.com/aliyun-oss-sdk-6.17.1.min.js"></script>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        
        .container {
            background: white;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        
        h1 {
            color: #333;
            text-align: center;
            margin-bottom: 30px;
        }
        
        .upload-section {
            margin-bottom: 30px;
            padding: 20px;
            border: 2px dashed #ddd;
            border-radius: 8px;
            text-align: center;
        }
        
        .file-input {
            margin: 15px 0;
        }
        
        .file-input input[type="file"] {
            margin: 10px 0;
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
            width: 100%;
        }
        
        .btn {
            background: #007bff;
            color: white;
            padding: 10px 20px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            margin: 5px;
            font-size: 14px;
        }
        
        .btn:hover {
            background: #0056b3;
        }
        
        .btn:disabled {
            background: #ccc;
            cursor: not-allowed;
        }
        
        .progress {
            width: 100%;
            height: 20px;
            background: #f0f0f0;
            border-radius: 10px;
            overflow: hidden;
            margin: 15px 0;
            display: none;
        }
        
        .progress-bar {
            height: 100%;
            background: #28a745;
            width: 0%;
            transition: width 0.3s ease;
        }
        
        .log {
            background: #f8f9fa;
            padding: 15px;
            border-radius: 5px;
            margin: 15px 0;
            font-family: monospace;
            font-size: 12px;
            max-height: 300px;
            overflow-y: auto;
            border: 1px solid #ddd;
        }
        
        .success {
            color: #28a745;
        }
        
        .error {
            color: #dc3545;
        }
        
        .info {
            color: #17a2b8;
        }
        
        .credential-info {
            background: #e9ecef;
            padding: 15px;
            border-radius: 5px;
            margin: 15px 0;
            font-size: 12px;
            word-break: break-all;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>OSS 直传测试页面</h1>
        
        <div class="upload-section">
            <h3>选择文件上传</h3>
            <div class="file-input">
                <input type="file" id="fileInput" multiple>
                <div>
                    <button class="btn" onclick="getCredentials()">获取上传凭证</button>
                    <button class="btn" onclick="uploadFiles()" id="uploadBtn" disabled>开始上传</button>
                    <button class="btn" onclick="clearLog()">清空日志</button>
                </div>
            </div>
            
            <div class="progress" id="progressContainer">
                <div class="progress-bar" id="progressBar"></div>
            </div>
            
            <div id="uploadStatus"></div>
        </div>
        
        <div class="credential-info" id="credentialInfo" style="display: none;">
            <h4>OSS凭证信息:</h4>
            <div id="credentialDetails"></div>
        </div>
        
        <div class="log" id="logContainer">
            <div class="info">等待操作...</div>
        </div>
    </div>

    <script>
        let ossCredentials = null;
        let selectedFiles = [];
        
        // 从URL参数获取token
        function getTokenFromURL() {
            const urlParams = new URLSearchParams(window.location.search);
            const token = urlParams.get('token');
            if (!token) {
                log('❌ URL中未找到token参数,请在URL中添加?token=xxx', 'error');
                return null;
            }
            return token;
        }
        
        // 获取OSS上传凭证
        async function getCredentials() {
            log('正在获取OSS上传凭证...', 'info');
            
            const token = getTokenFromURL();
            if (!token) {
                return;
            }
            
            try {
                const response = await fetch(`https://ms-ai.chongzhiling.com/file/upload-policy?token=${token}`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': `Bearer ${token}`
                    },
                    body: {}
                });
                
                const result = await response.json();
                
                if (result.code === 200) {
                    ossCredentials = result.data;
                    log('✅ OSS凭证获取成功', 'success');
                    
                    // 显示凭证信息
                    document.getElementById('credentialInfo').style.display = 'block';
                    document.getElementById('credentialDetails').innerHTML = `
                        <strong>Bucket:</strong> ${ossCredentials.bucket}<br>
                        <strong>Endpoint:</strong> ${ossCredentials.endpoint}<br>
                        <strong>AccessKeyId:</strong> ${ossCredentials.accessKeyId}<br>
                        <strong>到期时间:</strong> ${ossCredentials.expiration}
                    `;
                    
                    // 启用上传按钮
                    document.getElementById('uploadBtn').disabled = false;
                } else {
                    log(`❌ 获取凭证失败: ${result.msg}`, 'error');
                }
            } catch (error) {
                log(`❌ 网络错误: ${error.message}`, 'error');
            }
        }
        
        // 上传文件到OSS
        async function uploadFiles() {
            const fileInput = document.getElementById('fileInput');
            const files = fileInput.files;
            
            if (!files.length) {
                log('❌ 请先选择文件', 'error');
                return;
            }
            
            if (!ossCredentials) {
                log('❌ 请先获取OSS凭证', 'error');
                return;
            }
            
            const progressContainer = document.getElementById('progressContainer');
            const progressBar = document.getElementById('progressBar');
            
            progressContainer.style.display = 'block';
            
            for (let i = 0; i < files.length; i++) {
                const file = files[i];
                const progress = ((i + 1) / files.length) * 100;
                
                log(`📤 正在上传文件 ${i + 1}/${files.length}: ${file.name}`, 'info');
                
                try {
                    const fileUrl = await uploadSingleFile(file);
                    log(`✅ 文件上传成功: ${file.name}`, 'success');
                    log(`🔗 文件链接: ${fileUrl}`, 'success');
                } catch (error) {
                    log(`❌ 文件上传失败 ${file.name}: ${error.message}`, 'error');
                }
                
                progressBar.style.width = `${progress}%`;
            }
            
            log('🎉 所有文件上传完成!', 'success');
        }
        
        // 上传单个文件
        async function uploadSingleFile(file) {
            const timestamp = Date.now();
            const objectKey = `ms-ai-b/${timestamp}_${file.name}`;
            
            try {
                // 使用OSS SDK方式上传(需要引入OSS SDK)
                const client = new OSS({
                    accessKeyId: ossCredentials.accessKeyId,
                    accessKeySecret: ossCredentials.accessKeySecret,
                    stsToken: ossCredentials.securityToken,
                    bucket: ossCredentials.bucket,
                    endpoint: ossCredentials.endpoint
                });
                
                const result = await client.put(objectKey, file);
                return result.url;
                
            } catch (sdkError) {
                log('OSS SDK方式失败,尝试表单上传...', 'info');
                
                // 备用方案:表单上传
                const ossUrl = `https://${ossCredentials.bucket}.${ossCredentials.endpoint}`;
                
                const formData = new FormData();
                formData.append('key', objectKey);
                formData.append('OSSAccessKeyId', ossCredentials.accessKeyId);
                formData.append('x-oss-security-token', ossCredentials.securityToken);
                formData.append('file', file);
                
                const response = await fetch(ossUrl, {
                    method: 'POST',
                    body: formData,
                    mode: 'cors'
                });
                
                if (!response.ok) {
                    const errorText = await response.text();
                    throw new Error(`HTTP ${response.status}: ${errorText}`);
                }
                
                return `https://${ossCredentials.bucket}.${ossCredentials.endpoint}/${objectKey}`;
            }
        }
        
        // 日志函数
        function log(message, type = 'info') {
            const logContainer = document.getElementById('logContainer');
            const timestamp = new Date().toLocaleTimeString();
            const logEntry = document.createElement('div');
            logEntry.className = type;
            logEntry.innerHTML = `[${timestamp}] ${message}`;
            logContainer.appendChild(logEntry);
            logContainer.scrollTop = logContainer.scrollHeight;
        }
        
        // 清空日志
        function clearLog() {
            document.getElementById('logContainer').innerHTML = '<div class="info">日志已清空...</div>';
        }
        
        // 文件选择事件
        document.getElementById('fileInput').addEventListener('change', function(e) {
            const files = e.target.files;
            if (files.length > 0) {
                log(`📁 已选择 ${files.length} 个文件:`, 'info');
                for (let i = 0; i < files.length; i++) {
                    const file = files[i];
                    const sizeStr = file.size > 1024 * 1024 
                        ? `${(file.size / 1024 / 1024).toFixed(2)} MB`
                        : `${(file.size / 1024).toFixed(2)} KB`;
                    log(`  - ${file.name} (${sizeStr})`, 'info');
                }
            }
        });
    </script>
</body>
</html>