Dio: Upload mp4

Created on 10 Oct 2019  ·  9Comments  ·  Source: flutterchina/dio

I'm trying to upload a mp4 using s3 signed urls. the request is performed but the data uploaded is not correctly encoded.

final data = some_file.readBytesSync();
_dio
        .put(
          url.toString(),
          options: Options(contentType: 'video/mp4'),
          data: data,
        )

What I'm doing wrong ?

Thanks,

stale

Most helpful comment

Hi,
You can simply pass stream in body and no needs to use anything else then dio to upload file,
I was facing the same issue, and solved by passing stream in body as below,

Convert file into byte[] and then bytes to Stream, .finalize() returns stream

List<int> imageBytes = File(path).readAsBytesSync();
var file = MultipartFile.fromBytes(imageBytes).finalize();

Dio().post('api/', data: file);

All 9 comments

Yeah im getting the same exact problem and have no idea how to just write legit binary. I would also prefer streaming it but I fear thats even more of a nightmare.

I end up writing something like this

Hello World!

And the output is something like

[72,101,108,108,111,32,82,101,115,111,117,114,99,101,32,99,52,47,56,68,47,70,57,57,54,47,55,53,68,56,52,52,97,65,97,50,57,56,53,66,53,56,47,48,101,57,100,57,69,52,48,56,56,56,100,47,70,69,56,50,51,69,99,69,50,55,98,99,47,57,48,52,99,52,49,53,54,49,51,67,50,54,51,100,51,47,53,50,98,100,49,57,55,51,47,52,50,57,52,48,49,55,50,56,101,49,100,55,55,51,57,32,40,73,32,117,115,101,100,32,116,104,101,32,117,114,108,32,104,116,116,112,115]

The only other option is form data but that sprinkles a bunch of form post data on the uploaded file.

I found the culprit

in dio.dart

if (data is Stream) {
...
} else if (data is FormData) {
...
} else { // Our data is either List<int> or Uint8List

  // The data is converted to string here, as data is inside options.
  String _data = await transformer.transformRequest(options); 
  if (options.requestEncoder != null) {
    bytes = options.requestEncoder(_data, options);
  } else {
    //Default convert to utf8
    bytes = utf8.encode(_data); // Then this is what i think im seeing in the resulting files
  }
 ...
}

The problem is even if you setup a request encoder, it has the entire request in it already converted to a string. And if you setup a custom transformer, you must return a string. So basically im not writing an http client while using an http client.

Can Dio NOT encode literally everything to utf8? Why is this so difficult to do? Am I overlooking something?

Alright I have figured out two ways around this issue without using Dio

Option 1: Stream It

Stream the file to the presigned url using StreamedRequest

static Future<bool> uploadPresignedStreamed(String url, File file) async {
  try {
    int tl = file.lengthSync();
    final int tx = tl;
    Stream<List<int>> stream = file.openRead();
    assert(stream != null);
    http.StreamedRequest request = http.StreamedRequest(
      "PUT",
      Uri.parse(url),
    )..headers.addAll({
        "Content-Type": "octet-stream",
        "Content-Disposition": 'attachment; filename="resource"',
        "Content-Encoding": "identity",
        "Content-Length": tx.toString()
      });
    stream.listen((l) {
      request.sink.add(l);
      tl -= l.length;
    }).onDone(() {
      request.sink.close();
      L.i("Upload Stream Finished");
    });
    L.i("Uploading " + path.basename(file.path));
    final http.BaseResponse response = await request.send().then(http.Response.fromStream);

    if (response.statusCode == 200) {
      L.i("Uploaded " + path.basename(file.path));
      return true;
    } else {
      L.e("Failed to Upload " + path.basename(file.path) + " Status Code: " + response.statusCode.toString());
      return false;
    }
  } catch (e) {
    L.e("Failed to Upload " + path.basename(file.path) + " because: " + e.toString());
    return false;
  }
}

Option 2: Read First, Send After

This option is simpler, but you can seriously run out of resources if your files are too large.

static Future<bool> uploadPresignedSlow(String url, File file) async {
  try {
    L.i("Uploading " + path.basename(file.path));
    var response = await http.put(url, body: file.openRead(), headers: {
      "Content-Type": "octet-stream",
      "Content-Disposition": 'attachment; filename="resource"',
      "Content-Encoding": "identity",
      "Content-Length": file.lengthSync().toString()
    });

    if (response.statusCode == 200) {
      L.i("Uploaded " + path.basename(file.path));
    } else {
      L.e("Failed to upload " + path.basename(file.path) + " Status Code: ${response.statusCode}");
      return false;
    }
  } catch (e) {
    L.e("Failed to Upload " + path.basename(file.path) + " because: " + e.toString());
    return false;
  }
  return true;
}

Hi,
You can simply pass stream in body and no needs to use anything else then dio to upload file,
I was facing the same issue, and solved by passing stream in body as below,

Convert file into byte[] and then bytes to Stream, .finalize() returns stream

List<int> imageBytes = File(path).readAsBytesSync();
var file = MultipartFile.fromBytes(imageBytes).finalize();

Dio().post('api/', data: file);

cannot access MultipartFile class as i want to use dio version 1.0.3 and i am unable to pass the binary data of a file in it. Can anyone help for solving this issue?

cannot access MultipartFile class as i want to use dio version 1.0.3 and i am unable to pass the binary data of a file in it. Can anyone help for solving this issue?

@parthvora-techypanther : you will have to use latest dio version.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If this is still an issue, please make sure it is up to date and if so, add a comment that this is still an issue to keep it open. Thank you for your contributions.

I found this line to avoid body from being encoded to utf8:

https://github.com/flutterchina/dio/blob/5e3eb797a4202b9aca0acc74d325c10523b79584/dio/lib/src/dio.dart#L1012

So, i try to give a requestEncoder to Options and it works.

Options(
  requestEncoder: (_, __) => file.readAsBytesSync()
)

@huangbinjie thank you, it works for me now

Was this page helpful?
0 / 5 - 0 ratings