Improve attachments queue performance

This commit is contained in:
LittleSheep 2024-08-01 23:10:19 +08:00
parent 27c60fc8cb
commit b2a6ca7244
3 changed files with 69 additions and 43 deletions

View File

@ -115,10 +115,10 @@ class PostEditorController extends GetxController {
builder: (context) => AttachmentEditorPopup( builder: (context) => AttachmentEditorPopup(
usage: 'i.attachment', usage: 'i.attachment',
initialAttachments: attachments, initialAttachments: attachments,
onAdd: (value) { onAdd: (int value) {
attachments.add(value); attachments.add(value);
}, },
onRemove: (value) { onRemove: (int value) {
attachments.remove(value); attachments.remove(value);
}, },
), ),

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
@ -26,11 +27,40 @@ class AttachmentUploaderController extends GetxController {
RxDouble progressOfUpload = 0.0.obs; RxDouble progressOfUpload = 0.0.obs;
RxList<AttachmentUploadTask> queueOfUpload = RxList.empty(growable: true); RxList<AttachmentUploadTask> queueOfUpload = RxList.empty(growable: true);
Timer? _progressSyncTimer;
double _progressOfUpload = 0.0;
void _syncProgress() {
progressOfUpload.value = _progressOfUpload;
queueOfUpload.refresh();
}
void _startProgressSyncTimer() {
if (_progressSyncTimer != null) {
_progressSyncTimer!.cancel();
}
_progressSyncTimer = Timer.periodic(
const Duration(milliseconds: 500),
(_) => _syncProgress(),
);
}
void _stopProgressSyncTimer() {
if (_progressSyncTimer == null) return;
_progressSyncTimer!.cancel();
_progressSyncTimer = null;
}
void enqueueTask(AttachmentUploadTask task) { void enqueueTask(AttachmentUploadTask task) {
if (isUploading.value) throw Exception('uploading blocked'); if (isUploading.value) throw Exception('uploading blocked');
queueOfUpload.add(task); queueOfUpload.add(task);
} }
void enqueueTaskBatch(Iterable<AttachmentUploadTask> tasks) {
if (isUploading.value) throw Exception('uploading blocked');
queueOfUpload.addAll(tasks);
}
void dequeueTask(AttachmentUploadTask task) { void dequeueTask(AttachmentUploadTask task) {
if (isUploading.value) throw Exception('uploading blocked'); if (isUploading.value) throw Exception('uploading blocked');
queueOfUpload.remove(task); queueOfUpload.remove(task);
@ -40,8 +70,8 @@ class AttachmentUploaderController extends GetxController {
isUploading.value = true; isUploading.value = true;
progressOfUpload.value = 0; progressOfUpload.value = 0;
_startProgressSyncTimer();
queueOfUpload[queueIndex].isUploading = true; queueOfUpload[queueIndex].isUploading = true;
queueOfUpload.refresh();
final task = queueOfUpload[queueIndex]; final task = queueOfUpload[queueIndex];
final result = await _rawUploadAttachment( final result = await _rawUploadAttachment(
@ -51,13 +81,13 @@ class AttachmentUploaderController extends GetxController {
null, null,
onProgress: (value) { onProgress: (value) {
queueOfUpload[queueIndex].progress = value; queueOfUpload[queueIndex].progress = value;
queueOfUpload.refresh(); _progressOfUpload = value;
progressOfUpload.value = value;
}, },
); );
queueOfUpload.removeAt(queueIndex); queueOfUpload.removeAt(queueIndex);
queueOfUpload.refresh(); _stopProgressSyncTimer();
_syncProgress();
isUploading.value = false; isUploading.value = false;
@ -70,9 +100,10 @@ class AttachmentUploaderController extends GetxController {
isUploading.value = true; isUploading.value = true;
progressOfUpload.value = 0; progressOfUpload.value = 0;
_startProgressSyncTimer();
for (var idx = 0; idx < queueOfUpload.length; idx++) { for (var idx = 0; idx < queueOfUpload.length; idx++) {
queueOfUpload[idx].isUploading = true; queueOfUpload[idx].isUploading = true;
queueOfUpload.refresh();
final task = queueOfUpload[idx]; final task = queueOfUpload[idx];
final result = await _rawUploadAttachment( final result = await _rawUploadAttachment(
@ -82,20 +113,20 @@ class AttachmentUploaderController extends GetxController {
null, null,
onProgress: (value) { onProgress: (value) {
queueOfUpload[idx].progress = value; queueOfUpload[idx].progress = value;
queueOfUpload.refresh(); _progressOfUpload = (idx + value) / queueOfUpload.length;
progressOfUpload.value = (idx + value) / queueOfUpload.length;
}, },
); );
progressOfUpload.value = (idx + 1) / queueOfUpload.length; _progressOfUpload = (idx + 1) / queueOfUpload.length;
onData(result); onData(result);
queueOfUpload[idx].isUploading = false; queueOfUpload[idx].isUploading = false;
queueOfUpload[idx].isCompleted = false; queueOfUpload[idx].isCompleted = false;
queueOfUpload.refresh();
} }
queueOfUpload.clear(); queueOfUpload.clear();
queueOfUpload.refresh(); _stopProgressSyncTimer();
_syncProgress();
isUploading.value = false; isUploading.value = false;
} }

View File

@ -56,12 +56,10 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
final medias = await _imagePicker.pickMultiImage(); final medias = await _imagePicker.pickMultiImage();
if (medias.isEmpty) return; if (medias.isEmpty) return;
for (final media in medias) { _enqueueTaskBatch(medias.map((x) {
final file = File(media.path); final file = File(x.path);
_enqueueTask( return AttachmentUploadTask(file: file, usage: widget.usage);
AttachmentUploadTask(file: file, usage: widget.usage), }));
);
}
} }
Future<void> _pickVideoToUpload() async { Future<void> _pickVideoToUpload() async {
@ -88,11 +86,9 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
List<File> files = result.paths.map((path) => File(path!)).toList(); List<File> files = result.paths.map((path) => File(path!)).toList();
for (final file in files) { _enqueueTaskBatch(files.map((x) {
_enqueueTask( return AttachmentUploadTask(file: x, usage: widget.usage);
AttachmentUploadTask(file: file, usage: widget.usage), }));
);
}
} }
Future<void> _takeMediaToUpload(bool isVideo) async { Future<void> _takeMediaToUpload(bool isVideo) async {
@ -159,7 +155,11 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
_isFirstTimeBusy = false; _isFirstTimeBusy = false;
return; return;
} else { } else {
_attachments = List.filled(widget.initialAttachments.length, null); _attachments = List.filled(
widget.initialAttachments.length,
null,
growable: true,
);
} }
setState(() => _isBusy = true); setState(() => _isBusy = true);
@ -239,20 +239,9 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
fontFamily: 'monospace', fontFamily: 'monospace',
), ),
), ),
FutureBuilder( Text(
future: element.file.length(), 'In queue #${index + 1}',
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Text(
'- Bytes',
style: TextStyle(fontSize: 12),
);
}
return Text(
_formatBytes(snapshot.data!),
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
);
},
), ),
], ],
), ),
@ -410,6 +399,13 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
} }
} }
void _enqueueTaskBatch(Iterable<AttachmentUploadTask> tasks) {
_uploadController.enqueueTaskBatch(tasks);
if (_isAutoUpload) {
_startUploading();
}
}
void _startUploading() { void _startUploading() {
_uploadController.performUploadQueue(onData: (r) { _uploadController.performUploadQueue(onData: (r) {
widget.onAdd(r.id); widget.onAdd(r.id);
@ -433,11 +429,10 @@ class _AttachmentEditorPopupState extends State<AttachmentEditorPopup> {
child: DropTarget( child: DropTarget(
onDragDone: (detail) async { onDragDone: (detail) async {
if (_uploadController.isUploading.value) return; if (_uploadController.isUploading.value) return;
for (final file in detail.files) { _enqueueTaskBatch(detail.files.map((x) {
_enqueueTask( final file = File(x.path);
AttachmentUploadTask(file: File(file.path), usage: widget.usage), return AttachmentUploadTask(file: file, usage: widget.usage);
); }));
}
}, },
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,