Skip to content

Commit

Permalink
compose: Include content-type in image-upload requests
Browse files Browse the repository at this point in the history
Fixes: zulip#829
  • Loading branch information
chrisbobbe committed Jul 24, 2024
1 parent 3dad2d3 commit 52a0f80
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 5 deletions.
34 changes: 29 additions & 5 deletions lib/widgets/compose_box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/zulip_localizations.dart';
import 'package:image_picker/image_picker.dart';
import 'package:mime/mime.dart';

import '../api/exception.dart';
import '../api/model/model.dart';
Expand Down Expand Up @@ -423,11 +424,17 @@ class _FixedDestinationContentInput extends StatelessWidget {
/// A convenience class to represent data from the generic file picker,
/// the media library, and the camera, in a single form.
class _File {
_File({required this.content, required this.length, required this.filename});
_File({
required this.content,
required this.length,
required this.filename,
required this.mimeType,
});

final Stream<List<int>> content;
final int length;
final String filename;
final String? mimeType;
}

Future<void> _uploadFiles({
Expand Down Expand Up @@ -474,14 +481,14 @@ Future<void> _uploadFiles({
}

for (final (tag, file) in uploadsInProgress) {
final _File(:content, :length, :filename) = file;
final _File(:content, :length, :filename, :mimeType) = file;
Uri? url;
try {
final result = await uploadFile(store.connection,
content: content,
length: length,
filename: filename,
mimeType: null, // TODO(#829)
mimeType: mimeType,
);
url = Uri.parse(result.uri);
} catch (e) {
Expand Down Expand Up @@ -579,7 +586,17 @@ Future<Iterable<_File>> _getFilePickerFiles(BuildContext context, FileType type)

return result.files.map((f) {
assert(f.readStream != null); // We passed `withReadStream: true` to pickFiles.
return _File(content: f.readStream!, length: f.size, filename: f.name);
String? mimeType;
if (f.path != null) {
mimeType = lookupMimeType(f.path!,
headerBytes: f.bytes?.take(defaultMagicNumbersMaxLength).toList());
}
return _File(
content: f.readStream!,
length: f.size,
filename: f.name,
mimeType: mimeType,
);
});
}

Expand Down Expand Up @@ -664,7 +681,14 @@ class _AttachFromCameraButton extends _AttachUploadsButton {
}
final length = await result.length();

return [_File(content: result.openRead(), length: length, filename: result.name)];
return [_File(
content: result.openRead(),
length: length,
filename: result.name,
mimeType: result.mimeType
// TODO pass headerBytes (what's a good way to get that?)
?? lookupMimeType(result.path),
)];
}
}

Expand Down
6 changes: 6 additions & 0 deletions test/widgets/compose_box_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ void main() {

testBinding.pickFilesResult = FilePickerResult([PlatformFile(
readStream: Stream.fromIterable(['asdf'.codeUnits]),
// TODO test inference of mimeType from initial bytes, when
// it can't be inferred from path
path: '/private/var/mobile/Containers/Data/Application/foo/tmp/image.jpg',
name: 'image.jpg',
size: 12345,
Expand All @@ -304,6 +306,7 @@ void main() {
..field.equals('file')
..length.equals(12345)
..filename.equals('image.jpg')
..contentType.asString.equals('image/jpeg')
..has<Future<List<int>>>((f) => f.finalize().toBytes(), 'contents')
.completes((it) => it.deepEquals(['asdf'.codeUnits].expand((l) => l)))
);
Expand All @@ -328,6 +331,8 @@ void main() {
composeBoxController.topicController!.value = const TextEditingValue(text: 'some topic');

testBinding.pickImageResult = XFile.fromData(
// TODO test inference of mimeType when it's missing here
mimeType: 'image/jpeg',
utf8.encode('asdf'),
name: 'image.jpg',
length: 12345,
Expand All @@ -352,6 +357,7 @@ void main() {
..field.equals('file')
..length.equals(12345)
..filename.equals('image.jpg')
..contentType.asString.equals('image/jpeg')
..has<Future<List<int>>>((f) => f.finalize().toBytes(), 'contents')
.completes((it) => it.deepEquals(['asdf'.codeUnits].expand((l) => l)))
);
Expand Down

0 comments on commit 52a0f80

Please sign in to comment.