2

Problem Statement

Hi, basically, what I want is to send a file through a POST to a python server-side Flask API.

The goal is to provide the user with a file picker interface, and after him/her choosing the desired .txt file, send it through Dio using a POST request.

A button (Widget) should call this uploadFile() function and everything should be handle by it


Code

Here is my current code:

import 'package:dio/dio.dart';
import 'dart:html';
import 'package:http/http.dart' as http;

BaseOptions options = new BaseOptions(
  baseUrl: "http://localhost:5000",
  connectTimeout: 5000,
  receiveTimeout: 3000,
);
InputElement uploadInput = FileUploadInputElement();

void uploadFile() async {
  uploadInput.click();
  uploadInput.onChange.listen((e) {
    // read file content as dataURL
    final files = uploadInput.files;
    final reader = new FileReader();

    if (files.length == 1) {
      final file = files[0];

        reader.onLoad.listen((e) {
          sendFile(reader.result);
        });

        reader.readAsDataUrl(file);
      }
  });
}

sendFile(file) {
  var dio = new Dio(options);
  FormData formData = FormData.fromMap({"file": MultipartFile.fromBytes(file), "fileName": 'data.txt'});
  dio.post('/upload', data: formData);
  //dio.post('/upload', data: FormData.fromMap({'file': 'aaaa'}));
}

Trial Outcome

The problem here is that FileReader does not have a readAsBytes method, therefore I can't send the file as a Multipart file using the fromBytes method.

The only methods FileReader has are: readAsDataUrl, readAsArrayBuffer and readAsText I don't want to read the .txt body to send it, I want to send the actual file.

Other tries

  • I have also tried using file_picker but it seems that it does not support Flutter Web

Reference Links

My try is based on: Is there any plugin or way to upload file to server using flutter web?

and some others that I could not find again.

React Inspiration

The way I've managed to do it in React was:

FileUploader.js

export const UploadButton = ({ handler }) => (
    <button onClick={handler.uploadTextFile}>Upload Image</button>
);

export const FileChooser = ({ handler }) => (
    <input
        ref={ref => {
            handler.uploadInput = ref;
        }}
        type="file"
    />
);

Handler.js:

async uploadTextFile(ev) {
        ev.preventDefault();

        this.fileName = uuid.v4();
        const data = new FormData();
        data.append("file", this.uploadInput.files[0]);
        data.append("fileName", this.fileName);
        data.append("setOfWords", this.words);

        await fetch("http://localhost:5000/upload", {
            method: "POST",
            body: data
        });

Cry

Please, help? I am stuck on it for more than 4 days and could not yet figure out how to solve this in Flutter Web...

Victor Maricato
  • 672
  • 8
  • 25

1 Answers1

7

Managed to do it:

As described in this blog post:

1. I've made FileReader read as DataUrl

reader.readAsDataUrl(file);

2. Treat data, transforming it into Byte

  Uint8List _bytesData =
      Base64Decoder().convert(file.toString().split(",").last);
  List<int> _selectedFile = _bytesData;

3. To read it, use Multipart.readFromBytes():

file = http.MultipartFile.fromBytes('file', _selectedFile,
          contentType: new MediaType('application', 'octet-stream'),
          filename: "text_upload.txt")

4. To send it, use http.request

var url = Uri.parse("http://localhost:5000/upload");
var request = new http.MultipartRequest("POST", url);

request.files.add(http.MultipartFile.fromBytes('file', _selectedFile,
      contentType: new MediaType('application', 'octet-stream'),
      filename: "text_upload.txt"));

request.send().then((response) {
    print("test");
    print(response.statusCode);
    if (response.statusCode == 200) print("Uploaded!");
});

Final Code Looks Like This

import 'dart:convert';
import 'dart:typed_data';
import 'dart:html';
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';

InputElement uploadInput = FileUploadInputElement();

void uploadFile() async {
  uploadInput.draggable = true;
  uploadInput.click();
  uploadInput.onChange.listen((e) {
    // read file content as dataURL
    final files = uploadInput.files;
    final reader = new FileReader();

    if (files.length == 1) {
      final file = files[0];

      reader.onLoad.listen((e) {
        sendFile(reader.result);
      });

      reader.readAsDataUrl(file);
    }
  });
}

sendFile(file) {
  var url = Uri.parse("http://localhost:5000/upload");
  var request = new http.MultipartRequest("POST", url);
  Uint8List _bytesData =
      Base64Decoder().convert(file.toString().split(",").last);
  List<int> _selectedFile = _bytesData;

  request.files.add(http.MultipartFile.fromBytes('file', _selectedFile,
      contentType: new MediaType('application', 'octet-stream'),
      filename: "text_upload.txt"));

  request.send().then((response) {
    print("test");
    print(response.statusCode);
    if (response.statusCode == 200) print("Uploaded!");
  });
}
Victor Maricato
  • 672
  • 8
  • 25
  • 1
    contentType: new MediaType('application', 'octet-stream') Does the trick for me. Thanks a lot – Febin K R Aug 04 '20 at 07:28
  • Hi, What about large files. Let's say 8GB. I don't think List _selectedFile = _bytesData; works in that case. – AzureIP Nov 23 '21 at 10:23