Closed Saeeed-B closed 2 years ago
I implemented some parts of it in my mobile application. My implementation is not a full implementation. It can do a simple upload, download, and display an uploaded image.
Here is my implementation ostrio_file_collection.dart
import 'dart:convert';
import 'dart:io';
import 'package:open_file/open_file.dart';
import 'package:path/path.dart' as p;
import 'package:http/http.dart' as http;
import 'package:dart_meteor/dart_meteor.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:telemedicine_consumer_app/helpers/meteor_random.dart';
final mimeMapping = {
'jpg': 'image/jpeg',
'png': 'image/png',
};
class OstrioFileCollection {
final String collectionName;
final MeteorClient meteor;
OstrioFileCollection(
{this.collectionName = 'MeteorUploadFiles', required this.meteor});
Map<String, String> get _methodName => {
'_Abort': '_FilesCollectionAbort_$collectionName',
'_Write': '_FilesCollectionWrite_$collectionName',
'_Start': '_FilesCollectionStart_$collectionName',
'_Remove': '_FilesCollectionRemove_$collectionName',
};
Future<dynamic> insert({
required XFile file,
String? fileId,
Map<String, dynamic> meta = const {},
int chunkSize = -1,
Function(dynamic)? onUploaded,
Function()? onStart,
Function(MeteorError)? onError,
Function(double)? onProgress,
Function(dynamic)? onBeforeUpload,
bool autoStart = true,
}) async {
if (await file.length() <= 0) {
throw Exception('Can\'t upload empty file');
}
final fileSize = await file.length();
final fileContent = await file.readAsBytes();
final base64FileContent = base64Encode(fileContent);
final base64ContentSize = base64FileContent.length;
if (chunkSize == -1) {
// dynamic
chunkSize = (base64ContentSize / 1000).ceil();
if (chunkSize < 327680) {
chunkSize = 327680;
} else if (chunkSize > 1048576) {
chunkSize = 1048576;
}
}
// Base64 chunk size
chunkSize = (chunkSize / 4).floor() * 4;
fileId ??= MeteorRandom.id();
var totalChunk = (base64ContentSize / chunkSize).ceil();
var fileExtension =
file.name.substring(file.name.lastIndexOf('.') + 1).trim();
var fileType = mimeMapping[fileExtension] ?? '';
final fileData = {
'size': fileSize,
'type': fileType,
'name': file.name,
'meta': meta,
};
final optsStart = {
'file': fileData,
'fileId': fileId,
'chunkSize': (chunkSize / 4 * 3).toInt(),
'fileLength': totalChunk,
};
if (onBeforeUpload != null) {
onBeforeUpload(fileData);
}
try {
if (onStart != null) {
onStart();
}
if (onProgress != null) {
onProgress(0.0);
}
await meteor.call(_methodName['_Start']!, args: [optsStart]);
for (var i = 0; i < totalChunk; i++) {
var start = i * chunkSize;
var end = (i + 1) * chunkSize;
if (end > base64FileContent.length) {
end = base64FileContent.length;
}
var chunkData = base64FileContent.substring(start, end);
var padding = chunkData.length % 4;
while (padding > 0) {
chunkData += '=';
padding--;
}
final optsWrite = {
'fileId': fileId,
'chunkId': i + 1,
'binData': chunkData,
};
await meteor.call(_methodName['_Write']!, args: [optsWrite]);
if (onProgress != null) {
onProgress((i + 1) / totalChunk);
}
}
final optsEof = {'eof': true, 'fileId': fileId};
final uploadResult = await meteor.call(
_methodName['_Write']!,
args: [optsEof],
);
if (onProgress != null) {
onProgress(1.0);
}
if (onUploaded != null) {
onUploaded(uploadResult);
}
return uploadResult;
} on MeteorError catch (error) {
if (onError != null) {
onError(error);
}
rethrow;
}
}
}
class MeteorTokenHttpClient extends http.BaseClient {
final String sessionId;
final http.Client _inner;
MeteorTokenHttpClient(this.sessionId, this._inner);
@override
Future<http.StreamedResponse> send(http.BaseRequest request) {
request.headers['cookie'] = 'x_mtok=$sessionId';
return _inner.send(request);
}
}
Future<bool> presentDownloadableFile({
required MeteorClient meteor,
required String uriString,
Function(double)? progress,
}) async {
final filename = p.basename(uriString);
final uri = Uri.parse(uriString);
final client = MeteorTokenHttpClient(
meteor.connection.sessionId!,
http.Client(),
);
final request = http.Request('GET', uri);
final response = await client.send(request);
var received = 0;
if (response.statusCode == 200) {
final tempDir = await getTemporaryDirectory();
final outputFile = File('${tempDir.path}/$filename');
final ioSink = outputFile.openWrite();
await response.stream.map((chunk) {
received += chunk.length;
var prog = received / response.contentLength!;
if (progress != null) {
progress(prog);
}
return chunk;
}).pipe(ioSink);
await OpenFile.open(outputFile.path);
return true;
}
return false;
}
Here is an example of sendFile
function when user tap on the UI.
void sendFile() async {
var source = await showImageSourceDialog(context);
var imagePicker = ImagePicker();
if (source != null) {
XFile? file = await imagePicker.pickImage(source: source);
if (file != null) {
final fileCollection = OstrioFileCollection(
collectionName: 'chatUploadedFiles',
meteor: meteor,
);
fileCollection.insert(
file: file,
meta: {
'visitId': widget.visit!.id,
},
);
}
}
}
@tanutapi How can we use the file package and display a photo or something? https://github.com/veliovgroup/Meteor-Files
This package works with file collections. To get the link of a file, the link method must be called a document. like this : https://github.com/VeliovGroup/Meteor-Files#stream-files.
But this method was not detected in your package.