5

I want to upload a file via a non-multipart/form-data request like this:

POST http://127.0.0.1/upload
Cache-Control: no-cache

< /path/to/file/in/disk

(I tested and it successfully uploaded a file from JetBrain Rider's REST client to my REST endpoint.)

There is a StreamedRequest class in http package but I didn't find any constructor or setter to plug a byte stream or file content into it.

How do I use StreamedRequest to upload a file in Dart?

phqb
  • 351
  • 3
  • 14

3 Answers3

8

I'm just going to post phqb's solution in case the pastebin link goes down:

final file = new File(filePath);

final streamedRequest =
    new http.StreamedRequest('POST', Configurations.getUserContentsApiUri('/upload'))
      ..headers.addAll({
        'Cache-Control': 'no-cache',
        'Authorization': 'Bearer $uploadToken',
      });
streamedRequest.contentLength = await file.length();
file.openRead().listen((chunk) {
  print(chunk.length);
  streamedRequest.sink.add(chunk);
}, onDone: () {
  streamedRequest.sink.close();
});

await streamedRequest.send();
print('after response');
Mark
  • 4,749
  • 7
  • 44
  • 53
  • How to write to `streamedRequest.stream` according to https://pub.dev/documentation/http/latest/http/StreamedRequest-class.html ? I tried `streamedRequest.sink.addStream()` but didn't work. – Shayan Sep 30 '20 at 22:59
4

In some older code of mine I use

  /// Send a POST request to the Docker service.
  Future<http.ByteStream> _streamRequestStream(
      String path, Stream<List<int>> stream,
      {Map<String, String> query}) async {
    assert(stream != null);
    final url = serverReference.buildUri(path, query);
    final request = new http.StreamedRequest('POST', url)
      ..headers.addAll(headersTar);
    stream.listen(request.sink.add);
    final http.BaseResponse response =
        await request.send().then(http.Response.fromStream);
    if (response.statusCode < 200 || response.statusCode >= 300) {
      throw new DockerRemoteApiError(
          response.statusCode, response.reasonPhrase, null);
    }
    return (response as http.StreamedResponse).stream;
  }

which might do what you want

https://github.com/bwu-dart/bwu_docker/blob/master/lib/src/remote_api.dart#L160-L178

It uses the http package

import 'package:http/http.dart' as http;
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Thanks, it requested successfully and the file was uploaded. But how do I listen to the response? The `await request.send()` seemed never to be finished. (In this [script](https://pastebin.com/raw/spMi1EXS) the `print` statement is never been executed.) – phqb Jul 06 '18 at 07:55
  • 4
    I figured it out. Closing the `sink` on file stream's `onDone` solved the problem. – phqb Jul 06 '18 at 10:18
  • How to write to `streamedRequest.stream` according to https://pub.dev/documentation/http/latest/http/StreamedRequest-class.html ? I tried `streamedRequest.sink.addStream()` but didn't work. – Shayan Sep 30 '20 at 22:59
  • 1
    How about request.sink.add as I did? – Günter Zöchbauer Oct 01 '20 at 03:13
  • But how do I send a POST request with this body with a streamedRequest for example `body: {'hello': 'world'}`. sink.add() only accepts List how do I send an object? – Shayan Oct 04 '20 at 17:43
  • Can you write an answer here Thanks very much https://stackoverflow.com/questions/64178155/flutter-http-streamedresponse-stream-and-streamedresponse-sink – Shayan Oct 05 '20 at 11:49
1

Update:

A day after posting this I realized that this is not actually measuring the upload progress, but only the progress of reading the bytes from the local JSON payload, which is almost instantaneous. If/when I figure out how to actually measure the upload progress, I'll update this answer.


Original Answer:

This works for me:

import 'dart:convert';
import 'package:http/http.dart' as http;

// ...

final jsonPayload = {'base64File': 'abc123', 'something': 'else'};

// We are using a StreamedRequest so we can track the upload progress
final streamedRequest = http.StreamedRequest("POST", apiUri);
streamedRequest.headers['content-type'] = 'application/json';

// Length transferred (to calculate upload progress)
var transferredLength = 0;
// Upload progress (from 0.0 to 1.0)
var uploadProgress = 0.0;
// The stringified JSON payload
var stringEncodedPayload = jsonEncode(jsonPayload);
// Total length (to calculate upload progress)
var totalLength = stringEncodedPayload.length;

// Create a stream of the payload string
Stream.value(stringEncodedPayload)
  // Transform the string-stream to a byte stream (List<int>)
  .transform(utf8.encoder)
  // Start reading the stream in chunks, submitting them to the streamedRequest for upload
  .listen((chunk) {
    transferredLength += chunk.length;
    uploadProgress = transferredLength / totalLength;
    print("Chunk: ${chunk.length}, transferred: $transferredLength, progress: $uploadProgress");
    streamedRequest.sink.add(chunk);
  }, onDone: () {
    print("Done. Total: $totalLength, transferred: $transferredLength, progress: $uploadProgress");
    streamedRequest.sink.close();
  });

final result = await client.send(streamedRequest).then(http.Response.fromStream);


print("----------->");
print(result.statusCode);
print(result.body);
print("<-----------");

The output:

flutter: Chunk: 1024, transferred: 1024, progress: 0.0008807503580198599
flutter: Chunk: 1024, transferred: 2048, progress: 0.0017615007160397197
flutter: Chunk: 1024, transferred: 3072, progress: 0.0026422510740595796
...
flutter: Chunk: 1024, transferred: 1159168, progress: 0.9970094052784814
flutter: Chunk: 1024, transferred: 1160192, progress: 0.9978901556365013
flutter: Chunk: 1024, transferred: 1161216, progress: 0.9987709059945211
flutter: Chunk: 1024, transferred: 1162240, progress: 0.9996516563525409
flutter: Chunk: 405, transferred: 1162645, progress: 1.0
flutter: Done. Total: 1162645, transferred: 1162645, progress: 1.0
Johannes Fahrenkrug
  • 42,912
  • 19
  • 126
  • 165
  • I am wondering if you've actually managed to find the correct way to measure the upload progress (not the progress of reading bytes)? – Tom Raganowicz Jan 04 '23 at 12:02
  • @NeverEndingQueue That would be great. Do you have any additional findings you could share? – Johannes Fahrenkrug Jan 05 '23 at 07:40
  • 1
    I've given up on this and switched to Dio and not looking back. It is possible that this approach would work, but I haven't tested it: https://stackoverflow.com/a/50456212/2263395 – Tom Raganowicz Jan 07 '23 at 16:51