如何在Flutter应用中使用CloudFlare R2实现文件上传?
在Flutter中向CloudFlare R2上传文件的实现方案
CloudFlare R2兼容S3 API,下面提供两种可行的上传实现方式,直接解决请求头签名的问题:
方法一:利用S3兼容签名库(推荐)
通过Dart的AWS签名库自动处理请求头签名,配合http包完成上传,无需手动拼接签名逻辑。
步骤1:添加依赖
在pubspec.yaml中引入所需包:
dependencies: http: ^1.1.0 aws_signature_v4: ^0.4.0 crypto: ^3.0.3
步骤2:完整上传代码
import 'dart:io'; import 'package:http/http.dart' as http; import 'package:aws_signature_v4/aws_signature_v4.dart'; Future<void> uploadToR2({ required String accessKey, required String secretKey, required String bucketName, required String r2AccountId, // 你的R2账户ID,可在CloudFlare控制台获取 required File targetFile, required String objectKey, // 上传后在R2中的文件路径/名称 }) async { // 构建R2请求端点 final url = Uri.parse('https://$r2AccountId.r2.cloudflarestorage.com/$bucketName/$objectKey'); // 配置签名参数 final scope = AwsScope(region: 'auto', service: 's3'); final signer = AwsSignatureV4Signer( credentials: AwsCredentials(accessKey: accessKey, secretKey: secretKey), ); // 构建上传请求 final request = http.MultipartRequest('PUT', url) ..files.add(await http.MultipartFile.fromPath('file', targetFile.path)); // 生成带签名的请求 final signedRequest = await signer.sign(request, scope: scope); // 发送请求并处理响应 final response = await http.Response.fromStream(await signedRequest.send()); if (response.statusCode == 200 || response.statusCode == 201) { print('文件上传成功'); } else { print('上传失败:${response.statusCode} - ${response.body}'); } }
方法二:手动生成请求签名
如果不想依赖第三方库,可以手动实现AWS V4签名逻辑,核心是生成Authorization请求头:
完整手动签名上传代码
import 'dart:convert'; import 'dart:io'; import 'package:crypto/crypto.dart'; import 'package:http/http.dart' as http; // HMAC-SHA256加密工具方法 String hmacSha256(String key, String data) { final hmac = Hmac(sha256, utf8.encode(key)); return base64.encode(hmac.convert(utf8.encode(data)).bytes); } Future<void> uploadManuallyToR2({ required String accessKey, required String secretKey, required String bucketName, required String r2AccountId, required File targetFile, required String objectKey, }) async { final dateTime = DateTime.now().toUtc(); final dateFull = dateTime.toIso8601String().replaceAll('-', '').replaceAll(':', '').split('.').first + 'Z'; final dateShort = dateFull.substring(0, 8); final url = Uri.parse('https://$r2AccountId.r2.cloudflarestorage.com/$bucketName/$objectKey'); // 构建规范请求串 final canonicalRequest = [ 'PUT', '/$bucketName/$objectKey', '', 'host:${url.host}', 'x-amz-content-sha256:UNSIGNED-PAYLOAD', 'x-amz-date:$dateFull', '', 'host;x-amz-content-sha256;x-amz-date', 'UNSIGNED-PAYLOAD', ].join('\n'); // 生成签名密钥链 final kDate = hmacSha256('AWS4$secretKey', dateShort); final kRegion = hmacSha256(kDate, 'auto'); final kService = hmacSha256(kRegion, 's3'); final kSigning = hmacSha256(kService, 'aws4_request'); // 生成待签名字符串 final stringToSign = [ 'AWS4-HMAC-SHA256', dateFull, '$dateShort/auto/s3/aws4_request', sha256.convert(utf8.encode(canonicalRequest)).toString(), ].join('\n'); // 计算最终签名 final signature = hmacSha256(kSigning, stringToSign); // 组装Authorization头 final authHeader = 'AWS4-HMAC-SHA256 Credential=$accessKey/$dateShort/auto/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=$signature'; // 发送上传请求 final response = await http.put( url, headers: { 'Authorization': authHeader, 'x-amz-date': dateFull, 'x-amz-content-sha256': 'UNSIGNED-PAYLOAD', 'Content-Type': 'application/octet-stream', }, body: await targetFile.readAsBytes(), ); if (response.statusCode == 200 || response.statusCode == 201) { print('文件上传成功'); } else { print('上传失败:${response.statusCode} - ${response.body}'); } }
关键注意事项
- R2账户ID可在CloudFlare控制台的R2存储页面获取
- 确保你的AccessKey和SecretKey拥有目标存储桶的PUT对象权限
- 大文件建议使用分块上传(Multipart Upload),可参考S3分块上传逻辑适配R2
- 方法一的签名库已处理大部分边界情况,优先推荐使用
内容的提问来源于stack exchange,提问作者jitendra purohit
相关产品推荐
相关产品推荐

