51

I would like to know how to pick an Image from the users computer into my flutter web app for upload

Norbert
  • 6,874
  • 14
  • 40
  • 65
  • 1
    You can check my answer [here](https://stackoverflow.com/a/58121886/3043944) in another post. It basically uses the `FileUploadInputElement` from `dart:html` package. – Abhilash Chandran Nov 04 '19 at 09:36

10 Answers10

45

Using dart:html package directly in Flutter is not recommended.

Instead, use this package: https://pub.dev/packages/file_picker.

Example of how to use in Flutter Web:

class FileUploadButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      child: Text('UPLOAD FILE'),
      onPressed: () async {
        var picked = await FilePicker.platform.pickFiles();

        if (picked != null) {
          print(picked.files.first.name);
        }
      },
    );
  }
}

Note that FilePickerResult.path is not supported in Flutter Web.

jnt
  • 1,133
  • 14
  • 10
  • 2
    I am using this dependencies but not getting path .I have to upload it to firebase . THen what to use for flutter web to select file path for web – Rahul Kushwaha Feb 12 '21 at 07:41
  • 3
    This works in flutter web, but path is not needed, and is not implemented, as stated [explicitly](https://github.com/miguelpruivo/flutter_file_picker/wiki/API#-getdirectorypath) in the docs. Just use the `PlatformFile.bytes` property to access the bytes. – jnt Feb 12 '21 at 09:59
  • 2
    why i always get null on .bytes ? – Yogi Arif Widodo Nov 21 '21 at 11:49
  • 1
    Multipart request needs a Stream>. How to get this from .bytes? – AzureIP Nov 23 '21 at 09:33
  • 1
    ByteStream.fromBytes(bytes!.toList()) – AzureIP Nov 25 '21 at 14:29
  • 1
    it shows this error for me: Unsupported operation: Platform._operatingSystem – Benyamin Feb 09 '22 at 07:03
  • If you are getting error on this line `var picked = await FilePicker.platform.pickFiles();`, try flutter clean and flutter pub get – touhid udoy May 14 '23 at 00:59
33

I tried the code below and it worked.

first import 'dart:html';

// variable to hold image to be displayed 

      Uint8List uploadedImage;

//method to load image and update `uploadedImage`


    _startFilePicker() async {
    InputElement uploadInput = FileUploadInputElement();
    uploadInput.click();

    uploadInput.onChange.listen((e) {
      // read file content as dataURL
      final files = uploadInput.files;
      if (files.length == 1) {
        final file = files[0];
        FileReader reader =  FileReader();

        reader.onLoadEnd.listen((e) {
                    setState(() {
                      uploadedImage = reader.result;
                    });
        });

        reader.onError.listen((fileEvent) {
          setState(() {
            option1Text = "Some Error occured while reading the file";
          });
        });

        reader.readAsArrayBuffer(file);
      }
    });
    }

now just any Widget, like a button and call the method _startFilePicker()

Norbert
  • 6,874
  • 14
  • 40
  • 65
  • 1
    is there a way to detect if the file picker was canceled? – Abdul Rafay Sep 26 '20 at 19:04
  • 1
    @AbdulRafay my understanding for Web there isn't any cancelled callback. – C. Skjerdal Nov 30 '20 at 22:51
  • 1
    I'm finding it pretty hard to get an image uploaded with flutter web. This solution also gives error - : - Anyone know maybe why please? – Wesley Barnes Apr 16 '21 at 17:22
  • @WesleyBarnes did you find any solution. I am also stuck. I need to upload images from flutter web – Hadi Khan Apr 30 '23 at 10:29
  • @HadiKhan It also depends what file type you want to upload. I can't paste the code in this comment, maybe dm me. But, for web, I used -> final Uint8List fileBytes = file.bytes!; and file being -> PlatformFile file = result.files.first; result coming from FilePickerResult? result = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: ['jpg', 'png', 'pdf'], ); Use the FilePicker package and upload a Uint8List file you get from file.bytes – Wesley Barnes May 02 '23 at 16:18
  • @WesleyBarnes How to send it to api then? it's going to be formdata i know that much. Image data gets passed as instance of Multipart file which is throwing exception – Hadi Khan May 16 '23 at 07:55
  • @HadiKhan What data type does your API expect from you? I don't know about formdata, you typically upload to a storage bucket, that API's docs will tell you what data type you need – Wesley Barnes May 17 '23 at 13:59
8
import 'package:http/http.dart' as http;
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';

class FileUploadWithHttp extends StatefulWidget {
  @override
  _FileUploadWithHttpState createState() => _FileUploadWithHttpState();
}

class _FileUploadWithHttpState extends State<FileUploadWithHttp> {
  PlatformFile objFile = null;

  void chooseFileUsingFilePicker() async {
    //-----pick file by file picker,

    var result = await FilePicker.platform.pickFiles(
      withReadStream:
          true, // this will return PlatformFile object with read stream
    );
    if (result != null) {
      setState(() {
        objFile = result.files.single;
      });
    }
  }

  void uploadSelectedFile() async {
    //---Create http package multipart request object
    final request = http.MultipartRequest(
      "POST",
      Uri.parse("Your API URL"),
    );
    //-----add other fields if needed
    request.fields["id"] = "abc";

    //-----add selected file with request
    request.files.add(new http.MultipartFile(
        "Your parameter name on server side", objFile.readStream, objFile.size,
        filename: objFile.name));

    //-------Send request
    var resp = await request.send();

    //------Read response
    String result = await resp.stream.bytesToString();

    //-------Your response
    print(result);
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        children: [
          //------Button to choose file using file picker plugin
          RaisedButton(
              child: Text("Choose File"),
              onPressed: () => chooseFileUsingFilePicker()),
          //------Show file name when file is selected
          if (objFile != null) Text("File name : ${objFile.name}"),
          //------Show file size when file is selected
          if (objFile != null) Text("File size : ${objFile.size} bytes"),
          //------Show upload utton when file is selected
          RaisedButton(
              child: Text("Upload"), onPressed: () => uploadSelectedFile()),
        ],
      ),
    );
  }
}
Lad Khushbu
  • 81
  • 1
  • 2
5

I've tested this package and was very happy with the result imagePickerWeb it returns 3 different types it can be in the form of Image(widget for preview), byte, File(upload)

then you can use this to get the values

html.File _cloudFile;
 var _fileBytes;
 Image _imageWidget;
 
 Future<void> getMultipleImageInfos() async {
    var mediaData = await ImagePickerWeb.getImageInfo;
    String mimeType = mime(Path.basename(mediaData.fileName));
    html.File mediaFile =
        new html.File(mediaData.data, mediaData.fileName, {'type': mimeType});

    if (mediaFile != null) {
      setState(() {
        _cloudFile = mediaFile;
        _fileBytes = mediaData.data;
        _imageWidget = Image.memory(mediaData.data);
      });
    }

Uploading to firebase

don't forget to add this to your index.html

  <script src="https://www.gstatic.com/firebasejs/7.5.0/firebase-storage.js"></script>

Uploading to firebase

import 'package:firebase/firebase.dart' as fb;
    uploadToFirebase(File file) async {

    final filePath = 'temp/${DateTime.now()}.png';//path to save Storage
    try {
      fb
          .storage()
          .refFromURL('urlFromStorage')
          .child(filePath)
          .put(file);
    } catch (e) {
      print('error:$e');
    }
  }

See the documentation of the package if you still have problems

Dean Villamia
  • 576
  • 11
  • 24
4

The accepted answer is indeed outdated. Like jnt suggested, https://pub.dev/packages/file_picker is a handy package, when it comes to implementing an image upload using Flutter Web.

The problem I was facing is to get a base64 representation of an image, since I was using it to store images in Firestore. As we know, dart:io is not supported on Flutter Web and throws Unsupported operation: _Namespace error. Hence, using File and reading file's bytes was not an option. Luckily, the package provides API to convert the uploaded image to Uint8List. Here is my implementation:

import 'package:file_picker/file_picker.dart';

...

FilePickerResult? pickedFile;

...

void chooseImage() async {
    pickedFile = await FilePicker.platform.pickFiles();
    if (pickedFile != null) {
      try {
        setState(() {
          logoBase64 = pickedFile!.files.first.bytes;
        });
      } catch (err) {
        print(err);
      }
    } else {
      print('No Image Selected');
    }
}

In case you need to display the local image right away, use Image.memory.

Image.memory(logoBase64!);
Taiyr Begeyev
  • 547
  • 1
  • 9
  • 12
  • This is the easy part, you missed how to send it trough http and most importantly how to handle it on the server. – Santiago Jan 25 '22 at 18:17
  • Agree with Santiago. This is easy, it's converting the bytes back to a File to upload to AWS that is hard to find an answer for. – Jeff Frazier Mar 04 '22 at 20:30
3

i have this problem too;

i use https://pub.dev/packages/file_picker but in flutter web path not suppor;

you should to use bytes;

i save file bytes in var _fileBytes and use in request;

var request = http.MultipartRequest('POST', Uri.parse('https://.....com'));
request.headers.addAll(headers);
request.files.add(
   http.MultipartFile.fromBytes(
     'image', 
      await ConvertFileToCast(_fileBytes),
      filename: fileName,
      contentType: MediaType('*', '*')
   )
);
request.fields.addAll(fields);
var response = await request.send();

function ConvertFileToCast:

ConvertFileToCast(data){
  List<int> list = data.cast();
  return list;
}

it`s work for me :)

1

Update: amplify_storage_s3 support Flutter Web from v1.0.0.

...

I can share the way I upload image to AWS s3 from flutter web recently. May not exactly match the case who is looking for an answer here but I think it may inspire others somehow.

First I try to use amplify_storage_s3 package but it does not support Flutter Web yet for now. So I use basic http post instead.

The packages I use:

  • file_picker: For web, FileUploadInputElement (from html package) may do the same thing but I think using this package can make thing simpler.
  • dio: I'm not sure why I cannot use http's MultipartFile successfully so I use this instead. (maybe someone can provide a version using http package)
  • mine: transfer extension to mimetype

Code example:

import 'package:flutter/material.dart';
import 'package:dio/dio.dart' as dio;
import 'package:file_picker/file_picker.dart';
import 'package:mime/mime.dart';

class FileUploader extends StatelessWidget {
  const FileUploader({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          // 1. Pick an image file
          final filePicked = await FilePicker.platform.pickFiles();
          if (filePicked != null) {
            final file = filePicked.files.single; // PlatformFile
            final mimeType = lookupMimeType(file.name) ?? '';

            /// 2. Get presigned data somewhere
            const url = 'https://s3.amazonaws.com/......';
            final fields = {
              'bucket': '...',
              'X-Amz-Algorithm': 'AWS4-HMAC-SHA256',
              'X-Amz-Credential': '...',
              'X-Amz-Date': '...',
              'Policy': '...',
              'X-Amz-Signature': '...',
              'x-amz-meta-userid': '...',
              'Content-Type': mimeType,
              'file': dio.MultipartFile.fromBytes(file.bytes ?? []),
            };

            /// 3. Send file to AWS s3
            final formData = dio.FormData.fromMap(fields);
            await dio.Dio().post(url, data: formData);
          }
        },
        child: const Icon(Icons.upload),
      ),
    );
  }
}
yellowgray
  • 4,006
  • 6
  • 28
0

if anyone is wondering how to get it working on mobile and web :

var bytes;
      await file!.files.first.readStream!
          .map((asciiValue) => bytes = asciiValue)
          .toList();
      
 FormData body;
 final MultipartFile file = MultipartFile.fromBytes(bytes, filename: "file");
 MapEntry<String, MultipartFile> imageEntry = MapEntry("image", file);
 body.files.add(imageEntry);
HKTareen
  • 51
  • 5
0

Here is my working code to upload using dio. I use dio because it has a callback progress function.

class _FileUploadViewState extends State<FileUploadView> {
  @override
  void initState() {
    super.initState();
  }

  FilePickerResult? result;
  PlatformFile? file;
  Response? response;
  String? progress;
  String? percentage;
  Dio dio = Dio();

  selectFile() async {
    result =
        await FilePicker.platform.pickFiles(type: FileType.any, withData: true);

    if (result != null) {
      file = result?.files.single;
    }

    //print(file?.name);
    //print(file?.bytes?.length);
    //print(file?.size);
    //print(file?.extension);
    //print(file?.path);

    setState(() {});
  }

  Future<void> uploadFile(BuildContext context, User user) async {
    final navigator = Navigator.of(context);

    final storage = FlutterSecureStorage();

    String? token = await storage.read(key: 'jwt');

    final formData = FormData.fromMap(
      {
        'file': MultipartFile.fromBytes(file?.bytes as List<int>,
            filename: file?.name)
      },
    );

    dio.options.headers['content-Type'] = 'application/octet-stream';
    dio.options.headers["authorization"] = "Bearer $token";

    response = await dio.post(
      user.fileUrl,
      data: formData,
      onSendProgress: (int sent, int total) {
        percentage = (sent / total * 100).toStringAsFixed(2);
        progress = "$sent Bytes of $total Bytes - $percentage % uploaded";
        setState(
          () {},
        );
      },
    );

    if (response!.statusCode == 200) {
    ....

My go code for the server looks like this,

if err := r.ParseMultipartForm(64 << 20); err != nil {
    log.Println("error processing multipart form")
    log.Println(err)
    http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
    return
}

file, handler, err := r.FormFile("file")
markhorrocks
  • 1,199
  • 19
  • 82
  • 151
0

You can upload file in flutter web using file_picker package

Future<PlatformFile?> uploadFile() async {

  FilePickerResult? result = await FilePicker.platform.pickFiles(
    type: FileType.custom,
    onFileLoading: (status) => print(status),
    allowedExtensions: ['pdf', 'jpg', 'png'],
  );

  if (result != null) {
    return result.files.first;
  } else {
    return null;
  }
}

then you can call this method to get name and image in bytes

var result = await uploadFile();

//result.name => file name
//result.bytes! => file in bytes
                            
orcus
  • 71
  • 4