0

I am using flutter http https://pub.dev/packages/http package to download a file from my own webserver.

Below is the download function:

  download(String url, {String filename}) async {
    var client = http.Client();
    var request = new http.Request('GET', Uri.parse(url));
    var response = client.send(request);
    String dir = (await getApplicationDocumentsDirectory()).path;

    List<List<int>> chunks = new List();
    int downloaded = 0;

    response.asStream().listen((http.StreamedResponse r) {
      r.stream.listen((List<int> chunk) {
        // Display percentage of completion
        debugPrint('downloadPercentage: ${downloaded / r.contentLength * 100}');

        chunks.add(chunk);
        downloaded += chunk.length;
      }, onDone: () async {
        // Display percentage of completion
        debugPrint('downloadPercentage: ${downloaded / r.contentLength * 100}');

        // Save the file
        File file = new File('$dir/$filename');
        final Uint8List bytes = Uint8List(r.contentLength);
        int offset = 0;
        for (List<int> chunk in chunks) {
          bytes.setRange(offset, offset + chunk.length, chunk);
          offset += chunk.length;
        }
        await file.writeAsBytes(bytes);
        return;
      });
    });
  }

But StreamedResponse r is always null when I download the file from my webserver. I can download files from other web servers and it is not null.

For testing purpose, I used the link https://www.rarlab.com/rar/wrar590.exe

Therefore, I think the problem might be because of the settings on my webserver.

From WinRAR server

I/flutter (18386): IOStreamedResponse
I/flutter (18386): ResponseCode: 200
I/flutter (18386): {last-modified: Thu, 26 Mar 2020 10:03:25 GMT, date: Mon, 04 May 2020 11:47:34 GMT, accept-ranges: bytes, strict-transport-security: max-age=31536000;, content-length: 3007728, etag: "2de4f0-5a1bf18799540", content-type: application/octet-stream, server: Apache}

From my webserver:

I/flutter (29858): IOStreamedResponse
I/flutter (29858): ResponseCode: 200
I/flutter (29858): {connection: keep-alive, last-modified: Thu, 16 Apr 2020 20:22:20 GMT, set-cookie: __cfduid=d2789e7dce3f93d32c76e1d7874ffce1b1588599373; expires=Wed, 03-Jun-20 13:36:13 GMT; path=/; domain=.devoup.com; HttpOnly; SameSite=Lax, cf-request-id: 02817fd5a30000ddc752a1d200000001, transfer-encoding: chunked, date: Mon, 04 May 2020 13:36:13 GMT, access-control-allow-origin: *, vary: Accept-Encoding,User-Agent, content-encoding: gzip, cf-cache-status: DYNAMIC, content-type: application/zip, server: cloudflare, accept-ranges: bytes, cf-ray: 58e29c029952ddc7-SIN}
live-love
  • 48,840
  • 22
  • 240
  • 204
Xihuny
  • 1,410
  • 4
  • 19
  • 48
  • what do you need `response.asStream().listen` for? you have `var response = client.send(request);` - here `var response` is `Future` so simply await that `Future` to get `StreamedResponse` – pskink May 04 '20 at 06:30
  • @pskink because I want to show the download percentage. – Xihuny May 04 '20 at 11:17
  • `var response = await client.send(request); print(response.runtimeType); print(response.statusCode); print(response.headers); response.stream.listen((data) { print('chunk: ${data.length}'); });` what you see if you use this? – pskink May 04 '20 at 11:24
  • so as you can see you dont need `response.asStream().listen(...)` at all – pskink May 04 '20 at 11:29
  • @pskink status code is `404`. when I use your code, it only prints the first chunk received. I want also to get the filesize to the download file. In that way only I can show progress. – Xihuny May 04 '20 at 11:46
  • @pskink I have updated my question. Check the `response` & `headers` I get from both web servers when I made the request to download file – Xihuny May 04 '20 at 11:52
  • https://en.wikipedia.org/wiki/HTTP_404 – pskink May 04 '20 at 11:54
  • so what is the output of `curl -IL http://` command? also what you see if you replace `print('chunk: ${data.length}');` with `print(utf8.decode(data));`? – pskink May 04 '20 at 13:28
  • `[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: FormatException: Bad UTF-8 encoding 0x36 (at offset 11)` when I use `print(utf8.decode(data));` – Xihuny May 04 '20 at 13:40
  • also, status code returns `200`. I made a mistake in the uri – Xihuny May 04 '20 at 13:40
  • so you have 200, 200 is normal ok status, the code simply works – pskink May 04 '20 at 13:42
  • Now I only need to find the total size of the file. What I noticed is that `header` is not returning `content-length: xxx` – Xihuny May 04 '20 at 13:42
  • how to I make sure the header returns `content-length`? is it a setting from my webserver? – Xihuny May 04 '20 at 13:47
  • https://stackoverflow.com/questions/3304126/chunked-encoding-and-content-length-header – pskink May 04 '20 at 14:02
  • Thanks a lot. I added `SetEnv no-gzip 1` to `.htaccess` file and now it returns `content-length`. Also, as you suggested I modified the code without `response.asStream().listen` works perfectly. Can you add as an answer so that I can accept your answer? – Xihuny May 04 '20 at 15:00

2 Answers2

1

Sometimes the server doesn’t return contentLength . If that’s the case, then contentLength will be null.

If you have control of the server, like in your case, you can set the x-decompressed-content-length header with the file size before you send it. On the client side, you can retrieve contentLength like this:

final contentLength = double.parse(response.headers['x-decompressed-content-length']);

If you don't have control of the server, you can just show the number of bytes that are being downloaded, or if you know the file size in advance, you can use it to help calculate the % number of bytes downloaded.

live-love
  • 48,840
  • 22
  • 240
  • 204
0

Added SetEnv no-gzip 1 to .htaccess file in webserver and it returns content-length

Xihuny
  • 1,410
  • 4
  • 19
  • 48