Uppy загрузка ломается при перетаскивании нескольких файлов в композер

Здравствуйте,
после последнего обновления (мы работаем в управляемой среде) функциональность загрузки, похоже, сломана, хотя мы ничего особенного не делаем.

Мы просто регистрируем новый обработчик api.addComposerUploadHandler() внутри компонента темы.
Раньше это отлично работало при перетаскивании нескольких файлов в композер. Теперь в консоли появляются ошибки, которые, похоже, связаны с Uppy (чего мы не хотим).

Наш код действительно простой, но Uppy, похоже, всё равно вмешивается.

// Регистрация пользовательского обработчика загрузки для видео.
	api.addComposerUploadHandler(["mp4", "mov", "mkv", "avi", "m4v"], (file, editor) => {
		console.log("Обработка загрузки для", file.name);
	})

При перетаскивании трёх файлов (mkv, mov, mkv) в композер появляется сообщение об ошибке «ваш файл больше 4 МБ», которое мы изначально хотели обойти, так как загружаем всё в Gdrive.

В консоли Chrome выводится следующее:

Перетаскивание одного файла размером 50 МБ не вызывает ошибки «файл слишком большой», и наш файл обрабатывается корректно, как и ожидалось. Таким образом, ошибка возникает при работе с несколькими файлами, если один из них превышает лимит в 4 МБ (не уверен, где именно этот лимит установлен).

Спасибо за любую помощь в решении этой проблемы. Я думаю, что это связано с последним обновлением самого Discourse.

@martin

Привет, @Sören_Geier. В этой области недавно произошли некоторые изменения, хотя я пытался сохранить паритет с существующим обработчиком загрузки в композере. Мне просто нужно лучше понять ваш сценарий использования. Насколько я вижу, даже версия композера без Uppy, использующая api.addComposerUploadHandler, обрабатывает только один файл за раз:

https://github.com/discourse/discourse/blob/main/app/assets/javascripts/discourse/app/mixins/composer-upload.js#L215-L222

Так что, если вы перетащите несколько файлов, сработает обычный процесс, и, полагаю, в случае, описанном в исходном посте этой темы, вы просто упираетесь в ограничения размера файла, установленные для обычного процесса загрузки.

Что происходило раньше или что вы ожидаете, когда перетаскиваете несколько файлов одновременно? Было бы полезно увидеть код вашего компонента темы. Если вы не готовы делиться им публично, вы можете отправить его мне в личные сообщения здесь, на Meta.

Итак, просто для подтверждения: вы используете наш хостинг?

2 лайка

Спасибо за быстрый ответ, @martin.

Да, мы размещаемся у вас. Обычно, когда вы перетаскиваете файл в редактор, туда вставляется текст вроде «Обработка <имя_файла>». Кроме того, в случае использования API.addComposerUploadHandler(["mp4", "mov", "mkv", "avi", "m4v"]) это делается самим Discourse до передачи файла вашему кастомному обработчику. В какой-то момент вставка этого текста-заполнера перестала работать, и тогда я добавил этот код самостоятельно в свой обработчик:

composerController.model.appEvents.trigger("composer:insert-block", `[Processing: ${file.name}...]()`);

Затем возникла следующая проблема: наш обработчик перестал срабатывать, потому что вдруг расширения видеофайлов исчезли из настройки «Разрешённые расширения темы» — или мне пришлось заново добавить их там, чтобы всё снова заработало.

После этого я обнаружил проблему с перетаскиванием нескольких файлов, о которой упоминалось ранее.

У нас всё работало так, что я мог перетащить два и более файла без появления сообщений об ошибках. Это также казалось правильным, поскольку мы обходили всю логику валидации Discourse.

Вот соответствующие фрагменты кода:

Здесь я просто ожидаю, что Discourse передаст мне перетащенные файлы по одному.

// Регистрация кастомного обработчика загрузки для видео.
	api.addComposerUploadHandler(["mp4", "mov", "mkv", "avi", "m4v"], (file, editor) => {
		console.log("Handling upload for", file.name);
		sendToGDrive(file, api);
	})

Поскольку Discourse передавал нам файлы по отдельности, я создал промежуточную функцию, которая просто заполняет массив, а затем через таймаут запускает реальную функцию загрузки. Таким образом, я собираю переданные файлы от Discourse в свой собственный массив.

// Последовательный сбор всех перетащенных файлов, как сообщает обработчик Discourse.
function sendToGDrive(file, api) {
	clearTimeout(uploaderStartTimeout);
	filesHolder.push(file)
	const composerController = api.container.lookup("controller:composer");
	composerController.model.appEvents.trigger("composer:insert-block", `[Processing: ${file.name}...]()`);

	uploaderStartTimeout = setTimeout(function () {
		initFileSend(api);
	}, 300);
}

Затем я загружаю каждый файл в Google Диск по отдельности.

// Обработка каждого файла по отдельности.
async function initFileSend(api) {
	for (const file of filesHolder) {
		const content = await sendFileToGdrive(file, api, uploadFolderId);
	}
}

Наблюдаемые проблемы:

  • При перетаскивании «нескольких файлов» происходит валидация размера файла, тогда как при перетаскивании одного файла — нет.
1 лайк

Спасибо за этот подробный отчёт и связанный с ним код. Я уже придерживался подобной идеи для обработчиков загрузки: разрешать каждому файлу, соответствующему обработчику, попадать в очередь или пул, как вы сделали здесь, а затем загружать их все сразу или передавать в какой-либо другой интерфейс. Я считал ограничение «по одному файлу за раз» странным. Однако, судя по вашим словам, возможно, я неправильно понял, как работало это ограничение в старом обработчике загрузки композера.

Я проведу локальные тесты, чтобы посмотреть, как старый загрузчик (не Uppy) обрабатывал несколько файлов через обработчик загрузки, используя упрощённую версию функции, которую вы предоставили в компоненте темы. Затем я постараюсь обеспечить паритет между новым и старым подходом, поскольку он будет гораздо более гибким, чем ограничение одним файлом за раз.

На исправление может уйти немного времени, я займусь этим сегодня.

2 лайка

Хочу кратко обновить информацию: я подтвердил, что в обработчиках загрузки composer до интеграции Uppy проверка на загрузку только одного файла не совсем точна, поскольку jQuery File Uploader передаёт по этому пути только один файл за раз, даже если вы перетащите сразу несколько файлов. Это отличается от Uppy, который обрабатывает добавленные файлы группами. Предположение о том, что загружается только один файл за раз, встречается ещё в двух наших плагинах, использующих api.addComposerUploadHandler, так что, похоже, это общая проблема.

Как я уже говорил,我认为 лучшим решением будет позволить этому обработчику работать с несколькими файлами, которые затем можно будет сгруппировать и отправить дальше способом, который будет логичен для автора плагина или темы. Как минимум, я могу исправить предположение в обработчике загрузки Uppy о том, что за один раз может быть отправлен только один файл. Как только у меня появятся новые обновления, я обязательно напишу здесь снова.

2 лайка

Финальное обновление перед (моими) выходными. У меня есть исправление, которое должно быть слито на следующей неделе в начале недели, и оно вернёт «старый» способ работы, существовавший до Uppy, но уже внутри Uppy. Таким образом, ваша реализация снова начнёт работать корректно после этого:

Однако я также добавлю последующий PR, который изменит addComposerUploadHandler, чтобы передавать в обратный вызов обработчика несколько файлов в виде массива. Это избавит вас от необходимости настраивать очередь и обратные вызовы setTimeout для обработки нескольких файлов. Я считаю, что это в любом случае более правильный подход и общее улучшение API.

Таким образом, ваш обработчик будет выглядеть примерно так:

// Регистрация пользовательского обработчика загрузки для видео.
api.addComposerUploadHandler(
  ["mp4", "mov", "mkv", "avi", "m4v"],
  (files, editor) => {
    console.log("Обработка загрузки для", files.map((file) => file.name).join(", "));
    sendToGDrive(files, api);
  }
);
2 лайка

Отлично. Спасибо, что так быстро занялись этим!
Хороших и заслуженных выходных :blush:

2 лайка

@Sören_Geier Я только что объединил DEV: Send multiple files in batches to composer upload handlers when using uppy by martin-brennan · Pull Request #15124 · discourse/discourse · GitHub, который изменяет Uppy так, чтобы он отправлял несколько файлов одновременно в обработчик загрузки; вам теперь нужно обновить компонент вашей темы, чтобы он мог это обрабатывать :slight_smile:

3 лайка

Это здорово. Это ещё не развёрнуто, верно?

Вы используете наш стандартный хостинг? Если да, то всё должно быть уже развёрнуто :slight_smile:

3 лайка

Хорошо, я получил жалобы на то, что у пользователей, которые хотели загрузить файлы, что-то сломалось.
Я разобрался в ситуации и сам столкнулся с начальными проблемами при отправке файла в GDRIVE, так как просто передавал объект файла. Оказалось, что объект файла был обернутым в uppy представлением бинарного файла, выглядевшим так.

Чтобы получить доступ к нативному объекту файла, мне пришлось работать с files[0].data. (возможно, это разрушающее изменение?)

До этого изменения обработчик просто передавал нативный объект файла. Я предполагаю, что другие пользователи могут столкнуться с неработающей функциональностью из-за этого изменения, но я не уверен.

Теперь всё работает. Большое спасибо за быструю реакцию и поддержку!

3 лайка

Ох, вы правы, не понимаю, как я упустил это при недавнем рефакторинге! :sweat: Сегодня утром я внесу исправление, это займёт немного времени.

Редактирование: Исправление готово, оно должно быть развёрнуто на нашем стандартном хостинге в ближайшие несколько часов.

3 лайка

Отлично, мы также исправили наш код. Думаю, тему можно закрыть. Спасибо за исключительную помощь @martin

1 лайк

Никаких проблем! Я устроил беспорядок, так что справедливо, что должен его убрать :sweat_smile:

1 лайк