uploaded files to Azure are corrupted when using dio

谁说我不能喝 提交于 2021-02-08 10:13:17

问题


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


回答1:


I tried to upload a file using dio in Dart to Azure Blob Storage, and then download and print the content of the file, as the code below.

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

Then, I ran it and got the result as the figure below.

The content of the uploaded file as blob is the json string encoded from a Uint8List bytes from the funtion readAsBytesSync.

I researched the description and the source code of dio, actually I found dio is only suitable for sending the request body of json format, not for raw content as request body.

Fig 1. The default transformer apply for POST method

Fig 2. https://github.com/flutterchina/dio/blob/master/dio/lib/src/transformer.dart

So to fix it is to write a custom transformer class PutTransformerForRawData instead of the default one to override the function transformRequest, as the code below.

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

And to replace the default transformer via the code below.

var dio = Dio();
dio.transformer = PutTransformerForRawData();

Then, you can get the data via the code below.

var data = File(blobName).readAsBytesSync();

Or

var data = File(blobName).readAsStringSync();

Note: the custom transfer PutTransformerForRawData is only for uploading, please remove the download & print code Response response = await dio.get(url); print(response.data);, the default transformer seems to check the response body whether be json format, I got the exception as below when my uploaded file is my sample code.

Unhandled exception:
DioError [DioErrorType.DEFAULT]: FormatException: Unexpected character (at character 1)
import 'dart:typed_data';


来源:https://stackoverflow.com/questions/59293114/uploaded-files-to-azure-are-corrupted-when-using-dio

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!