Фиксим странное поведение плагина jQuery uploadify (и один баг)

в 8:25, , рубрики: bugs, javascript, jquery, uploadify

Так получилось, что я на своих сайтах использую плагин uploadify для jQuery — uploadify.com (хоть он уже устарел, флеш и все такое, но HTML5-версия у них уже платная). Плагин предоставляет мультизагрузку файлов, не перегружая страницу, что нам всем и надо. Однако оказалось, что в этом плагине не работает (и/или работает не так, как надо) функция проверки существования файлов перед отправкой на сервер.

Смотрим на нужный нам кусок кода. И видим там сразу четыре «бага». Ну, сразу не видим, да, давайте по порядку.

if (settings.checkExisting) {
 $.ajax({
  type    : 'POST',
  async   : false,
  url     : settings.checkExisting,
  data    : {filename: file.name},
  success : function(data) {
   if (data == 1) {
    var overwrite = confirm('A file with the name "' + file.name + '" already exists on the server.nWould you like to replace the existing file?');
    if (!overwrite) {
     this.cancelUpload(file.id);
     $('#' + file.id).remove();
     if (this.queueData.uploadQueue.length > 0 && this.queueData.queueLength > 0) {
      if (this.queueData.uploadQueue[0] == '*') {
       this.startUpload();
      } else {
       this.startUpload(this.queueData.uploadQueue.shift());
      }
     }
    }
   }
  }
 });
}

Проблема первая. Нужная опция называется не так, как она упомянута во всех туториалах в сети — checkExisiting, а не checkScript. Но это ладно, тут сам виноват — лучше читать родную документацию, а еще лучше код (в документации тоже напутано — в платной HTML5-версии плагина именно checkScript).

Проблема вторая. Мне нужна очень простая функция — если файл в нужном мне альбоме на сайте есть, то повторно его закачивать не надо, его надо просто молча пропустить и перейти к следующему, это сильно сэкономит трафик, а мне не надо помнить, какие файлы уже закачались, а какие нет — кормим uploadify 200 файлов, и он быстро докачает только те, которых нет. Но оказалось, что авторы сделали все не так, как мне надо.

Написал скрипт, запускаю — ой. На каждый существующий файл он выдает окошко с просьбой подтвердить загрузку этого файла. И так 200 раз.

Не пойдет.

(В settings.checkExisting хранится путь к моему скрипту, который должен вернуть 1, если такой файл уже существует и 0, если нет.)

В качестве обходного решения можно закомментировать var overwrite = confirm(..., а вместо этого просто написать var overwrite = false;

Я так сначала и сделал. Но потом решил, что это некрасиво и не гибко, так что чуть-чуть переписал эту фичу — в объект settings добавил новое свойство-опцию skipExisting, и поставил ее по умолчанию в true, что значит молча пропускать существующие файлы. Ну и в коде:

var overwrite = (this.settings.skipExisting) ? false : confirm('A file with the name "' + file.name + '" already exists on the server.nWould you like to replace the existing file?');

Проблема третья. Плагин во время закачивания файла передает не только его имя, но и произвольный набор данных в объекте formData. А вот при проверке существования файла почему-то ничего кроме его имени не передает. А мне ведь нужны те же formData, что и при закачке. Мне как минимум нужно знать номер альбома, в котором проверять файл. В разных альбомах могут встречаться повторяющиеся имена файлов. Не пойдет. Фиксим.

Меняем так, чтобы передавался в .ajax в качестве data не {filename: file.name},

data    : {filename: file.name},

А просто объект formData (из объекта settings):

data    : settings.formData,

Только теперь file.name надо в него добавить, иначе потеряется. Добавляем его перед вызовом метода .ajax:

settings.formData.filename = file.name;

ОК. Запускаю, все работает. Ан, нет! Не работает! Первый файл пытается пропустить, а потом:

image
Оба-на, а вот и баг.

Проблема четвертая. Авторы почему-то забыли про контекст. И метод .cancelUpload на this не срабатывает. Ну конечно, this-то не тот.

Вариантов фикса аж целых четыре — this можно запроксить, можно забиндить, можно просто закэшировать в переменной перед вызовом .ajax, но мы поступим еще проще, в методе jQuery.ajax есть опция context:

$.ajax({
 context : this,
 type    : 'POST',
 // и т.д...

Всё! Теперь плагин работает как надо.

Итак, новый код с изменениями:

if (settings.checkExisting) {
 settings.formData.filename = file.name;
 $.ajax({
  context : this,
  type    : 'POST',
  async   : false,
  url     : settings.checkExisting,
  data    : settings.formData,
  success : function(data) {
   if (data == 1) {
    var overwrite = (settings.skipExisting) ? false : confirm('A file with the name "' + file.name + '" already exists on the server.nWould you like to replace the existing file?');
    if (!overwrite) {
     this.cancelUpload(file.id);
     $('#' + file.id).remove();
     if (this.queueData.uploadQueue.length > 0 && this.queueData.queueLength > 0) {
      if (this.queueData.uploadQueue[0] == '*') {
       this.startUpload();
      } else {
       this.startUpload(this.queueData.uploadQueue.shift());
      }
     }
    }
   }
  }
 });
}

Ну и не забыть в опциях:

skipExisting    : true,               // Show prompt on exisiting files if false and just skip them if true

Можете скачать исправленную версию у меня, но я ее не минифицировал, пардон.

Автор: pnl

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js