azure - uploaded files to Azure are corrupted when using dio
问题描述
I'm trying to upload a file from my phone to azure blob storage as a BlockBlob with a SAS. I can get the file to upload, but it can't be opened once downloaded. The file gets corrupted somehow. I thought this was a content-type problem, but I have tried several different approaches to changing to content-type. Nothing has worked so far.
My code:
FileInfo _fileInfo = await filePicker(); // get the file path and file name
// my getUploadInfo fires a call to my backend to get a SAS.
// I know for a fact that this works because my website uses this SAS to upload files perfectly fine
UploadInfo uploadInfo = await getUploadInfo(_fileInfo.fileName, _fileInfo.filePath);
final bytes = File(_fileInfo.filePath).readAsBytesSync();
try {
final response = await myDio.put(
uploadInfo.url,
data: bytes,
onSendProgress:
(int sent, int total) {
if (total != -1) {
print((sent / total * 100).toStringAsFixed(0) + "%");
}
},
options:
dioPrefix.Options(headers: {
'x-ms-blob-type': 'BlockBlob',
'Content-Type': mime(_fileInfo.filePath),
})
);
} catch (e) {
print(e);
}
This code uploads a file just fine. But I can't open the file since it becomes corrupted. At first, I thought this was a Content-Type problem, so I've tried changing the content type header to: application/octet-stream
and multipart/form-data
as well. That doesn't work.
I've also tried to do
dioPrefix.FormData formData =
new dioPrefix.FormData.fromMap({
'file': await MultipartFile.fromFile(
_fileInfo.filePath,
filename: _fileInfo.fileName,
)
});
...
final response = await myDio.put(
uploadInfo.url,
data: formData, // This approach is recommended on the dio documentation
onSendProgress:
...
but this also corrupts the file. It gets uploaded, but I can't open it.
I have been able to successfully upload a file with this code, but with this approach I cannot get any type of response so I have no idea whether it uploaded successfully or not (Also, I can't get the progress of the upload):
try {
final data = imageFile.readAsBytesSync();
final response = await http.put( // here, response is empty no matter what i try to print
url,
body: data,
headers: {
'x-ms-blob-type': 'BlockBlob',
'Content-Type': mime(filePath),
});
...
Any help would be greatly appreciated. Thanks
解决方案
我尝试将dio
Dart 中使用的文件上传到 Azure Blob 存储,然后下载并打印文件内容,如下面的代码。
import 'package:dio/dio.dart';
import 'dart:io';
main() async {
var accountName = '<account name>';
var containerName = '<container name>';
var blobName = '<blob name>';
var sasTokenContainerLevel = '<container level sas token copied from Azure Storage Explorer, such as `st=2019-12-31T07%3A17%3A31Z&se=2020-01-01T07%3A17%3A31Z&sp=racwdl&sv=2018-03-28&sr=c&sig=xxxxxxxxxxxxxxxxxxxxxxxxxx`';
var url = 'https://$accountName.blob.core.windows.net/$containerName/$blobName?$sasTokenContainerLevel';
var data = File(blobName).readAsBytesSync();
var dio = Dio();
try {
final response = await dio.put(
url,
data: data,
onSendProgress:
(int sent, int total) {
if (total != -1) {
print((sent / total * 100).toStringAsFixed(0) + "%");
}
},
options: Options(
headers: {
'x-ms-blob-type': 'BlockBlob',
'Content-Type': 'text/plain',
})
);
print(response.data);
} catch (e) {
print(e);
}
Response response = await dio.get(url);
print(response.data);
}
然后,我运行它并得到如下图的结果。
Uint8List
作为 blob 的上传文件的内容是从函数中的字节编码的 json 字符串readAsBytesSync
。
我研究了它的描述和源代码dio
,实际上我发现dio
只适合发送json格式的请求体,不适用于原始内容作为请求体。
图 1. POST 方法的默认转换器应用
图 2. https://github.com/flutterchina/dio/blob/master/dio/lib/src/transformer.dart
所以修复它是编写一个自定义的转换器类PutTransformerForRawData
而不是默认的类来覆盖函数transformRequest
,如下面的代码。
import 'dart:typed_data';
class PutTransformerForRawData extends DefaultTransformer {
@override
Future<String> transformRequest(RequestOptions options) async {
if(options.data is Uint8List) {
return new String.fromCharCodes(options.data);
} else if(options.data is String) {
return options.data;
}
}
}
并通过下面的代码替换默认转换器。
var dio = Dio();
dio.transformer = PutTransformerForRawData();
然后,您可以data
通过下面的代码获取。
var data = File(blobName).readAsBytesSync();
或者
var data = File(blobName).readAsStringSync();
注意:自定义传输PutTransformerForRawData
仅用于上传,请删除下载和打印代码Response response = await dio.get(url); print(response.data);
,默认转换器似乎检查响应正文是否为json格式,当我上传的文件是我的示例代码时出现以下异常。
Unhandled exception:
DioError [DioErrorType.DEFAULT]: FormatException: Unexpected character (at character 1)
import 'dart:typed_data';
推荐阅读
- google-cloud-platform - 尽管允许防火墙中的端口,iperf3 仍无法正常工作
- python - Dash 输入、输出组件属性
- python - 谷歌云平台中的字符串到浮点转换问题
- angular - 当用户在 Angular 应用程序中手动输入访问被拒绝的 URL 时,如何避免导航到访问被拒绝的页面
- javascript - 我如何理解这 2 段代码?
- c - Ace Editor 是否可以使用 C 语言编译代码?
- python - Twitter/Reddit 帖子未正确嵌入 Django 模板
- tsql - 协调 TSQL PERCENT RANK() 与 Excel 等效项
- tensorflow - Tensorflow 嵌入层内部的网络结构是什么?
- kubernetes - 无法使用带有令牌验证的 Node-Red 来请求 kubernetesAPI