链接复制成功!
Web端直传OBS并设置上传回调
应用场景
常见的Web端上传方式是用户通过浏览器上传文件至应用服务器,再由应用服务器上传至OBS,如图1所示,数据需要在应用服务器中转,传输效率较低,且多任务同时上传时应用服务器压力很大。
本文介绍一种在Web端利用PostObject接口直传文件至OBS并设置上传回调的方法,即使用表单上传方式上传文件至OBS,上传成功后会回调指定的地址。如方案架构所示,该方案省去了在应用服务器中转文件这一步骤,由Web端直传文件至OBS,提高了传输效率,且不会对服务器产生压力,同时服务端签名后直传可以保证传输的安全性。
方案架构

整个Web端直传文件至OBS并设置上传回调的请求流程如下:
- 用户通过Web客户端向服务端发送上传安全策略请求;
- 服务端使用访问凭证和上传安全策略计算签名,然后向Web端返回上传安全策略和签名;
- 用户使用Web端构建的HTML表单上传文件至OBS并设置上传回调;
- OBS向服务端发送回调请求;
- 服务端返回响应至OBS;
- OBS返回上传结果至Web端。

安全策略(policy)的作用是限制表单上传的内容,例如规定表单上传对象的对象名前缀必须以“prefix01”开头,使用policy能够帮助您更好的管控桶中的文件。
资源和成本规划
操作流程
要实现Web端直传OBS并设置上传回调,需要以下三步:
- 创建桶并配置CORS规则:在OBS控制台创建一个桶,用于存储用户上传的文件;同时为桶配置跨域资源共享(CORS),以允许来自Web端的跨域名访问。
- 服务端生成签名:服务端使用访问凭证和上传策略(如:桶名称、对象名前缀、过期时间等)生成签名,授权用户在指定时间内完成文件上传。
- Web端通过表单上传对象并设置上传回调:Web端在HTML表单中构建请求并设置上传回调,此请求使用POST表单上传来直接调用OBS的服务端,实现文件上传。
实施步骤
步骤一:创建桶并配置CORS规则
在OBS控制台创建一个桶,用于存储用户上传的文件;同时为桶配置跨域资源共享(CORS),以允许来自Web端的跨域名访问。
1. 创建桶
如果您已有桶,可跳过本步骤直接配置CORS规则。
- 在OBS管理控制台左侧导航栏选择“桶列表”。
- 在页面右上角单击“创建桶”。
- 设置相关参数。
表2 创建桶参数说明 参数名称
示例
说明
区域
华北-北京四
桶所属的区域。
- 桶创建成功后,不支持变更区域,请谨慎选择。
- 请选择靠近您业务的区域创建桶,以降低网络时延,提高访问速度。
桶名称
post-callback-demo
创建桶时,需要设置合适的桶名称。
桶创建成功后,不支持修改桶名称。
OBS中桶按照DNS规范进行命名,DNS规范为全球通用规则,其具体命名规则如下:
- 需全局唯一,不能与已有的任何桶或并行文件系统(包含其他用户创建的)名称重复。删除桶后,立即创建同名桶或并行文件系统会创建失败,需要等待30分钟才能创建。
- 长度范围为3到63个字符。
- 支持小写字母、数字、中划线(-)、英文句号(.),且不能以中划线(-)或英文句号(.)开头及结尾。
- 禁止两个英文句号(.)相邻,禁止英文句号(.)和中划线(-)相邻。
- 禁止使用IP地址。
企业项目
default
将桶加入到企业项目中统一管理。如无特殊的企业项目划分和管理需求,此处可直接选择默认企业项目“default”。
如果您想要了解更多关于如何通过企业项目管理OBS桶,具体请参见创建桶中的“企业项目”参数说明。
- 单击右下角的“立即创建”,确认提示信息,并单击“确定”。
- 在“创建成功”弹窗,单击“确定”。在桶列表页可以看到新创建的桶,即表示创建成功。
2. 配置CORS规则
在通常的网页请求中,由于同源安全策略(Same-Origin Policy,SOP)的存在,不同域之间的网站脚本和内容是无法进行交互的。
跨域资源共享CORS是一种网络浏览器的规范机制,定义了一个域中加载的客户端Web应用程序与另一个域中的资源交互的方式。OBS支持CORS规范,允许跨域请求访问OBS中的资源。在OBS控制台配置CORS规则步骤如下:
- 在OBS管理控制台左侧导航栏选择“桶列表”。
- 在桶列表单击待操作的桶,进入对象页面。
- 在左侧导航栏,单击“数据安全 > CORS规则”。
- 单击“创建”系统弹出“创建CORS规则”弹窗。
- 设置相关参数。图3 创建CORS规则

表3 CORS规则参数说明 参数名称
示例
说明
允许的来源
*
指定允许的跨域请求的来源,即允许来自该域名下的请求访问该桶。
允许填写的字符范围:1~1024。
允许多条匹配规则,以回车换行为间隔。每个匹配规则允许使用最多一个“*”通配符。例如:
http://rds.example.com https://*.vbs.example.com
允许的方法
Get、Post、Put、Delete、Head
指定允许的跨域请求方法,即桶和对象的几种操作类型。包括:Get、Post、Put、Delete、Head。
允许的头域
*
可选参数,指定允许的跨域请求的头域。只有匹配上允许的头域中的配置,才被视为是合法的CORS请求。
允许填写的字符范围:1~1024。
允许的头域可设置多个,多个头域之间换行隔开,每行最多可填写一个*符号,不支持&、:、<、空格以及中文字符。
补充头域
- ETag
- x-obs-request-id
- x-obs-api
- Content-Type
- Content-Length
- Cache-Control
- Content-Disposition
- Content-Encoding
- Content-Language
- Expires
- x-obs-id-2
- x-reserved-indicator
- x-obs-version-id
- x-obs-copy-source-version-id
- x-obs-storage-class
- x-obs-delete-marker
- x-obs-expiration
- x-obs-website-redirect-location
- x-obs-restore
- x-obs-version
- x-obs-object-type
- x-obs-next-append-position
- x-obs-callback
可选参数,指定CORS响应中带的补充头域,给客户端提供额外的信息。
允许填写的字符范围:1~1024。
补充头域可设置多个,多个头域之间换行隔开,不支持*、&、:、<、空格以及中文字符。
缓存时间
300
请求来源的客户端可以缓存的CORS响应时间,以秒为单位,默认为100秒。
输入值范围:0~9999999。
- 单击“确定”。
“CORS规则”页签显示“创建CORS规则成功”提示即表示成功创建桶的CORS规则。CORS规则会在两分钟内生效。
步骤二:服务端生成签名
服务端使用访问凭证(AK/SK/securityToken[可选])和上传策略(如:桶名称、对象名前缀、过期时间等)生成签名,授权用户在指定时间内完成文件上传。获取或新建AK/SK,请参见访问密钥。

建议先将访问凭证(AK/SK/securityToken[可选])配置到环境变量,避免在代码中直接显示访问凭证而引起敏感信息泄露。
1. 配置环境变量
- Linux系统
- 在命令行中执行以下命令,将环境变量添加到~/.bashrc中。
echo "export ACCESS_KEY_ID='your access key id'" >> ~/.bashrc echo "export SECRET_ACCESS_KEY_ID='your secret access key id'" >> ~/.bashrc
- 在命令行中执行以下命令,使1的设置生效。
source ~/.bashrc
- 在命令行中执行以下命令,确认环境变量是否配置成功。
echo $ACCESS_KEY_ID echo $SECRET_ACCESS_KEY_ID
- 在命令行中执行以下命令,将环境变量添加到~/.bashrc中。
- Windows系统
- macOS系统
- 打开bash终端,执行以下命令,将环境变量添加到~/.bash_profile中。
echo "export ACCESS_KEY_ID='your access key id'" >> ~/.bash_profile echo "export SECRET_ACCESS_KEY_ID='your secret access key id'" >> ~/.bash_profile
- 在命令行中执行以下命令,使1的设置生效。
source ~/.bashrc
- 执行以下命令,确认环境变量是否配置成功。
echo $ACCESS_KEY_ID echo $SECRET_ACCESS_KEY_ID
- 打开bash终端,执行以下命令,将环境变量添加到~/.bash_profile中。
2. 计算签名
您可以通过使用编程语言的Web框架和OBS的SDK计算POST上传签名。POST表单上传是通过安全策略(policy)来限制表单上传的内容,例如规定表单上传对象的对象名前缀必须以“prefix01”开头,使用policy能够帮助您更好的管控桶中的文件。更多关于安全策略的内容,请参见基于浏览器上传的表单中携带签名。
参考如下Java示例代码获取访问凭证并计算签名:
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | @Controller public class FormUploadCallbackController { String bucket = "post-callback-demo"; // 以北京四的OBS地址为例 String host = "obs.cn-north-4.myhuaweicloud.com"; String endpoint = "https://" + host; String callbackUrl = "http://obs-demo.huaweicloud.com:12345/callback"; //限定上传到OBS的文件前缀 String prefix = "demo"; @GetMapping("/obs-post-callback-signature") public ResponseEntity<Map<String, String>> getSignature() throws Exception { // 您可以通过环境变量获取访问密钥AK/SK,也可以使用其他外部引入方式传入。如果使用硬编码可能会存在泄露风险。 // 您可以登录访问管理控制台获取访问密钥AK/SK String ak = System.getenv("ACCESS_KEY_ID"); String sk = System.getenv("SECRET_ACCESS_KEY_ID"); // 【可选】如果使用临时AK/SK和SecurityToken访问OBS,同样建议您尽量避免使用硬编码,以降低信息泄露风险。 // 您可以通过环境变量获取访问密钥AK/SK/SecurityToken,也可以使用其他外部引入方式传入。 // String securityToken = System.getenv("SECURITY_TOKEN"); // 创建ObsClient实例 // 【可选】使用临时AK/SK和SecurityToken初始化客户端 // try ( ObsClient obsClient = new ObsClient(ak, sk, securityToken, endpoint)){ // 使用永久AK/SK初始化客户端 try ( ObsClient obsClient = new ObsClient(ak, sk, endpoint)){ // 1. 创建策略条件 // 生成基于表单上传的请求 PostSignatureRequest request = new PostSignatureRequest(); // 设置表单参数 Map<String, Object> formParams = new HashMap<>(); // 设置对象ACL为公共读,需要其它配置可自行修改 formParams.put("x-obs-acl", "public-read"); request.setFormParams(formParams); List<String> conditions = new ArrayList<>(); // 设置Post上传请求的签名条件 conditions.add("[\"starts-with\", \"$key\", \"demo/\"]"); conditions.add("{\"bucket\": \"post-callback-demo\"}"); request.setConditions(conditions); // 设置表单上传请求有效期,单位:秒 request.setExpires(3600); PostSignatureResponse response = obsClient.createPostSignature(request); System.out.println("createPostSignature successfully"); // 获取表单上传请求参数 System.out.println("Policy:" + response.getPolicy()); System.out.println("Signature:" + response.getSignature()); Map<String, String> signResponse = new HashMap<>(); signResponse.put("prefix", prefix); signResponse.put("accessKeyId", ak); signResponse.put("policy", response.getPolicy()); signResponse.put("signature", response.getSignature()); // 【可选】如果使用临时AK/SK和SecurityToken访问OBS,此处返回securityToken // signResponse.put("securityToken",securityToken); signResponse.put("host", host); signResponse.put("bucket", bucket); signResponse.put("callbackUrl", callbackUrl); signResponse.put("callbackBody", "key=$(key)&hash=$(etag)&fname=$(fname)&fsize=$(size)"); signResponse.put("callbackBodyType", "application/json"); return ResponseEntity.ok(signResponse); } catch (ObsException e) { System.out.println("createPostSignature failed"); // 请求失败,打印http状态码 System.out.println("HTTP Code:" + e.getResponseCode()); // 请求失败,打印服务端错误码 System.out.println("Error Code:" + e.getErrorCode()); // 请求失败,打印详细错误信息 System.out.println("Error Message:" + e.getErrorMessage()); // 请求失败,打印请求id System.out.println("Request ID:" + e.getErrorRequestId()); System.out.println("Host ID:" + e.getErrorHostId()); e.printStackTrace(); throw e; } } } |
步骤三:Web端通过表单上传文件并设置上传回调
Web端接收到服务端返回的上传Policy和签名后,使用HTML表单构建请求并设置上传回调。此请求使用POST表单上传来直接调用OBS的服务端,实现文件上传。
Web端接收到服务端的响应示例如下:
{
"accessKeyId": "****************", // 密钥AK
"bucket": "post-callback-demo", // 需要上传的桶名
"callbackBodyType": "application/json", // 响应体格式
"signature": "24*******************u8=", // POST上传的签名
"prefix": "demo", // POST上传对象名前缀
"host": "obs.cn-north-4.myhuaweicloud.com", // POST上传host地址
"callbackUrl": "http://obs-demo.huaweicloud.com:23450/callback", // POST上传回调的地址
"policy": "eyJleHBpcmF***************************************ifV19", // POST上传policy
"callbackBody": "key=$(key)&hash=$(etag)&fname=$(fname)&fsize=$(size)" // POST上传回调的请求体
} 使用HTML表单构建请求并设置上传回调的示例代码如下:
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>华为云OBS表单上传</title> </head> <body> <h1>OBS表单上传回调示例</h1> <form> <div class="form-group"> <label for="file" class="form-label">选择文件:</label> <input type="file" class="form-control" id="file" name="file" required/> </div> <button name="submit" value="Upload" type="submit">上传到OBS</button> </form> <div id="result"></div> <script type="text/javascript"> document.addEventListener('DOMContentLoaded', function () { const form = document.querySelector("form"); const fileInput = document.querySelector("#file"); const resultDiv = document.getElementById("result"); form.addEventListener("submit", async (event) => { event.preventDefault(); if (!fileInput.files.length) { resultDiv.innerHTML = '<p style="color:red">请选择文件</p>'; return; } const file = fileInput.files[0]; if (!file) { alert('请选择一个文件再上传。'); return; } const filename = file.name; try { // 1. 获取签名参数 resultDiv.innerHTML = '获取签名中...'; const response = await fetch(`/obs-post-callback-signature`); const params = await response.json(); // 2. 构建表单数据 const formData = new FormData(); formData.append('key', params.prefix + "/" + filename); formData.append('x-obs-acl', 'public-read'); formData.append('policy', params.policy); formData.append('AccessKeyId', params.accessKeyId); formData.append('signature', params.signature); formData.append('callbackUrl', params.callbackUrl); formData.append('callbackBody', params.callbackBody); formData.append('callbackBodyType', params.callbackBodyType); // 【可选】如果服务端签名使用了SecurityToken,在Form表单中也需要设置 // formData.append('x-obs-security-token', params['x-obs-security-token']); formData.append('file', file); // 3. 提交到OBS resultDiv.innerHTML = '上传中...'; const obsEndpoint = `https://${params.bucket}.${params.host}`; const uploadResponse = await fetch(obsEndpoint, { method: 'POST', body: formData }); if (uploadResponse.status === 200) { resultDiv.innerHTML = `<p style="color:green">上传成功! OBS路径: ${params.key}</p> <p>回调通知将发送到: ${new URLSearchParams(atob(params.callback)).get('callbackUrl')}</p>`; } else { const error = await uploadResponse.text(); // 使用await resultDiv.innerHTML = `<p style="color:red">上传失败! 状态码: ${uploadResponse.status}</p> <p>${error}</p>`; } } catch (error) { resultDiv.innerHTML = `<p style="color:red">发生错误: ${error.message}</p>`; } }); }); </script> </body> </html> |
结果验证
HTML表单中包含一个文件选择框和一个上传按钮,用户可以选择想要上传的文件然后提交表单。
当表单提交时,JavaScript代码会请求服务器获取本次上传所需要的签名和回调信息,得到正确响应之后,会构造一个FormData对象, 然后填充所有必要数据,通过fetch方法发送POST请求到OBS的服务端,完成文件上传,上传成功后会回调callbackUrl设置的地址。

通过表单上传一个名为“demo-object”的文件至桶“post-callback-demo”中,上传后在OBS桶列表中的“post-callback-demo”中可以看到“demo-object”文件,即表示成功通过表单上传文件。



