Skip to content
philenius edited this page Jan 29, 2022 · 18 revisions

Q: Why am I able to select other file types even if I use a filter? Or why doesn't it filter at all?

I'm using FileType.custom with some extensions (eg. [csv, jpg]) and the file explorer doesn't respect it by allowing me picking other types.

A: When custom extensions are passed, those are translated into MIME types, then, an intent is sent to system requesting a file explorer to handle those and let you pick the files with the given restrictions — typically, the native one.

However, some Android OS versions (custom roms) not always have the native file explorer, neither you can't prevent the user to install a 3rd party on he’s own (such as ESExplorer). Those file explorers may be capable to handle the request but not always will (actually, unfortunately most of times won't) honour the MIME restrictions as they need to implement that feature and there's no way you can play around it. So, there's no warranty that fileExtensions will be respected and when you experience this issue, before reporting, try on an official emulator and see if it still happens.

So, because of that, either the package would have to throw an error when the user picked extensions that aren't suppose to, or, the developer can add this extra protection on their side which seems the more appropriate as you can iterate over the picked files and display a custom alert or similar.

Read more about it here.


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 != null && result.files.isNotEmpty) {
  final fileBytes = result.files.first.bytes;
  final 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:file_picker/file_picker.dart';
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.custom,
    allowedExtensions: [
      'jpg',
      'png',
      'mp4',
      'webm',
    ],
    withData: false,
    withReadStream: true,
  );

  if (result == null || result.files.isEmpty) {
    throw Exception('No files picked or file picker was canceled');
  }

  final file = result.files.first;
  final filePath = file.path;
  final mimeType = filePath != null ? lookupMimeType(filePath) : null;
  final contentType = mimeType != null ? MediaType.parse(mimeType) : null;

  final fileReadStream = file.readStream;
  if (fileReadStream == null) {
    throw Exception('Cannot read file from null stream');
  }
  final stream = http.ByteStream(fileReadStream);

  final uri = Uri.https('siasky.net', '/skynet/skyfile');
  final request = http.MultipartRequest('POST', uri);
  final multipartFile = http.MultipartFile(
    'file',
    stream,
    file.size,
    filename: file.name,
    contentType: contentType,
  );
  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