Skip to content
Miguel Ruivo edited this page Aug 10, 2021 · 18 revisions

Q: Why can't I access original file path?

A: This is one of the most asked questions and it actually was enough to make me create this FAQ section, as this doesn't represent an actual issue to fit Troubleshooting page.

Original paths were possible until file_picker 2.0.0 on Android, however, in iOS they were never possible at all since iOS wants you to make a cached copy and work on it. But, 2.0.0 introduced scoped storage support (Android 10) and with and per Android doc recommendations, files should be accessed in two ways:

  1. Pick files for CRUD operations (read, delete, edit) through files URI and use it directly — this is what you actually want but unfortunately isn’t supported by Flutter as it needs an absolute path to open a File descriptor;

  2. Cache the file temporarily for upload or similar, or just copy into your app’s persistent storage so you can later access it — this is what’s being done currently and even though you may have an additional step moving/copying the file after first picking, makes it safer and reliable to access any allowed file on any Android device.

You can check this blog post from Commonsware for more info.

TL;DR: after picking the file the first time, just move it to your app storage scope and use it from there onwards (you can use getApplicationDocumentsDirectory() method from path_provider to get your apps local storage.

Have in mind that currently file_picker is optimised to the point where if you pick twice the same file (with the same name) you’ll be probably picking the previous cached file, as it only will be cached the first time is picked.


Q: How do I know if user cancelled the picker?

A: On dart:io devices devices (Android, iOS and Desktop) the FilePickerResult will automatically return null if user opts to cancel/dismiss the picker.

However, on web, this is currently a limitation and there's no way to know whether the user cancelled it or not because there aren't any events fired from the platform end.


Q: How do I access the path on Web?

A: Paths aren't inaccessible from browsers since those provide fake paths. If you want to create a File instance to upload it somewhere, like FireStorage, you can do so with the bytes directly.

// get file
final result = await FilePicker.platform.pickFiles(type: FileType.any, allowMultiple: false);

if (result.files.first != null){
  var fileBytes = result.files.first.bytes;
  var fileName = result.files.first.name;
  
  // upload file
  await FirebaseStorage.instance.ref('uploads/$fileName').putData(fileBytes);
}

Thank you @devj3ns for providing the example.


Q: How do do I use withReadStream?

A: If you need to pick a heavy file to upload it somewhere, you can take advantage with withReadStream and read it in chunks. Check the example below.

import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';
import 'package:mime/mime.dart';

void main() async {
  final result = await FilePicker.platform.pickFiles(
    type: FileType.video,
    allowedExtensions: [
      'mp4',
      'webm',
    ],
    withData: false,
    withReadStream: true,
  );

  if (result != null) {
    final file = result.files.first;

    var uri = Uri.https('siasky.net', '/skynet/skyfile');

    var stream = http.ByteStream(file.readStream);

    var request = http.MultipartRequest('POST', uri);

    final mimeType = lookupMimeType(file.filename ?? '');

    var multipartFile = http.MultipartFile(
      'file',
      stream,
      file.size,
      filename: file.filename,
      contentType: mimeType == null ? null : MediaType.parse(mimeType),
    );

    request.files.add(multipartFile);

    final httpClient = http.Client();
    final response = await httpClient.send(request);

    if (response.statusCode != 200) {
      throw Exception('HTTP ${response.statusCode}');
    }

    final body = await response.stream.transform(utf8.decoder).join();

    print(body);
  }
}

Thank you @redsolver for providing the example.

Clone this wiki locally