Flutter & Firebase: Compression before upload image

Kamil Harasimowicz picture Kamil Harasimowicz · Oct 1, 2017 · Viewed 26.9k times · Source

I want to send photo selected by user in my app to Firebase Storage. I have a simple class with property _imageFile which is set like this:

File _imageFile;

_getImage() async {
    var fileName = await ImagePicker.pickImage();
    setState(() {
        _imageFile = fileName;
    });
}

after that I send photo like with this code:

final String rand1 = "${new Random().nextInt(10000)}";
final String rand2 = "${new Random().nextInt(10000)}";
final String rand3 = "${new Random().nextInt(10000)}";
final StorageReference ref = FirebaseStorage.instance.ref().child('${rand1}_${rand2}_${rand3}.jpg');
final StorageUploadTask uploadTask = ref.put(_imageFile);
final Uri downloadUrl = (await uploadTask.future).downloadUrl;
print(downloadUrl);

The problem is that the photos are often very large. Is there any method in Flutter/Dart to compress and resize photo before upload? I am ok with loss of quality.

Answer

Mans picture Mans · Mar 19, 2018

June 05, 2020 - Update

The image_picker plugin now supports an imageQuality paramater. You can do something like

ImagePicker imagePicker = ImagePicker();
PickedFile compressedImage = await imagePicker.getImage(
  source: ImageSource.camera,
  imageQuality: 85,
);

Doc for ImageQuality :

Returns a PickedFile object wrapping the image that was picked. The returned PickedFile is intended to be used within a single APP session. Do not save the file path and use it across sessions. The source argument controls where the image comes from. This can be either ImageSource.camera or ImageSource.gallery. Where iOS supports HEIC images, Android 8 and below doesn't. Android 9 and above only support HEIC images if used in addition to a size modification, of which the usage is explained below. If specified, the image will be at most maxWidth wide and maxHeight tall. Otherwise the image will be returned at it's original width and height. The imageQuality argument modifies the quality of the image, ranging from 0-100 where 100 is the original/max quality. If imageQuality is null, the image with the original quality will be returned. Compression is only supported for certain image types such as JPEG and on Android PNG and WebP, too. If compression is not supported for the image that is picked, a warning message will be logged. Use preferredCameraDevice to specify the camera to use when the source is ImageSource.camera. The preferredCameraDevice is ignored when source is ImageSource.gallery. It is also ignored if the chosen camera is not supported on the device. Defaults to CameraDevice.rear. Note that Android has no documented parameter for an intent to specify if the front or rear camera should be opened, this function is not guaranteed to work on an Android device. In Android, the MainActivity can be destroyed for various reasons. If that happens, the result will be lost in this call. You can then call getLostData when your app relaunches to retrieve the lost data.

Old Answer

or if you want to compress an image without using ImagePicker

I ran into this and was able to accomplish compression / resizing with the Dart image package along with path provider. You can look at dart image api and examples for other ways and more help.

Here's what I did:

import 'package:image/image.dart' as Im;
import 'package:path_provider/path_provider.dart';
import 'dart:math' as Math;

void compressImage() async {
  File imageFile = await ImagePicker.pickImage();
  final tempDir = await getTemporaryDirectory();
  final path = tempDir.path;
  int rand = new Math.Random().nextInt(10000);

  Im.Image image = Im.decodeImage(imageFile.readAsBytesSync());
  Im.Image smallerImage = Im.copyResize(image, 500); // choose the size here, it will maintain aspect ratio
  
  var compressedImage = new File('$path/img_$rand.jpg')..writeAsBytesSync(Im.encodeJpg(image, quality: 85));
}

Then I uploaded compressedImage to firebase storage. You can adjust the quality that the jpg is saved with using the quality property, in my case I chose 85 (out of 100).

Hope this helps! Let me know if you have any questions.