【问题标题】:uploaded files to Azure are corrupted when using dio使用 dio 时上传到 Azure 的文件已损坏
【发布时间】:2020-04-05 04:01:29
【问题描述】:

我正在尝试将文件从手机上传到 Azure Blob 存储,作为带有 SAS 的 BlockBlob。我可以让文件上传,但下载后无法打开。该文件以某种方式损坏。我认为这是一个内容类型问题,但我尝试了几种不同的方法来更改为内容类型。到目前为止没有任何效果。

我的代码:

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);
}

这段代码上传文件就好了。但我无法打开文件,因为它已损坏。起初,我认为这是一个 Content-Type 问题,所以我尝试将内容类型标头更改为:application/octet-streammultipart/form-data。这行不通。

我也试过了

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:
...

但这也会损坏文件。它已上传,但我无法打开它。

已经能够使用此代码成功上传文件,但使用这种方法我无法获得任何类型的响应,所以我不知道它是否成功上传(另外,我可以'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),
  });
...

任何帮助将不胜感激。谢谢

【问题讨论】:

  • 尝试使用 Azure 存储资源管理器下载文件。如果它也损坏了,那么就是您的上传代码损坏了。 PS:我从未听说过这样的事情(SAS 损坏了 blob 内容。我 99.999% 确定这是您的下载/上传代码)
  • 在使用 Azure 存储资源管理器时也已损坏。抱歉,标题误导了,我改一下。我也 99.99999% 确定这是上传代码。我只是不确定它的哪一部分搞砸了
  • 很遗憾,没有用于 Flutter 的 SDK。您最好尝试使用 REST gist.github.com/gregjhogan/ef37c38371193c8e9d08d867c05ad210 PS:使用代理来捕获请求并与您当前的请求进行比较,您将能够确定有什么不同
  • 嗨@wei,每当我使用这种方法时,我都会得到一个 404,知道我的 url 是正确的,并且我无法使用带有 SAS 令牌的连接字符串.. 有什么帮助吗?跨度>

标签: azure flutter dart azure-blob-storage dio


【解决方案1】:

我尝试在 Dart 中使用 dio 将文件上传到 Azure Blob Storage,然后下载并打印文件内容,如下代码所示。

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);
}

然后,我运行它,得到如下图的结果。

作为 blob 的上传文件的内容是从函数 readAsBytesSync 中的 Uint8List 字节编码的 json 字符串。

我研究了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';

【讨论】:

  • 嗨@Peter 当我的 url 在没有 SAS 令牌的情况下写入时出现 404 错误,当我的 url 附加了 SAS 令牌时出现 403 错误。知道为什么吗?我确定我的 url 是正确的,并且 SAS 没有过期。
  • @DCodes 你解决了这个问题吗?当我们将 sas 令牌添加到 url 403 错误时,我也遇到了同样的错误
  • @MallikarjunHampannavar 请检查一下:stackoverflow.com/questions/64196180/…
  • @DCodes 谢谢.. 我已经检查并获得 201 作为状态码.. 成功了吗?
  • 是的@MallikarjunHampannavar。 201 状态码表示请求成功,因此,已创建资源(例如新页面)..
猜你喜欢
  • 1970-01-01
  • 2020-01-14
  • 1970-01-01
  • 2013-10-24
  • 2023-03-20
  • 1970-01-01
  • 2017-06-27
  • 2018-06-21
  • 1970-01-01
相关资源
最近更新 更多