Внешняя загрузка больших файлов, возобновление точки останова (с исходным кодом)

опрос

предисловие

Будь то собеседование или реальная работа, у всех нас есть проблемы с загрузкой больших файлов. На самом деле, в моем предыдущем интервью меня также спрашивали, как справиться с загрузкой больших файлов (Excel), тогда я заикался, и все интервью закончилось провалом. Недавно я все это вспомнил и долго разбирался в этой демке, конечно, вся статья рассказывает не только о процессе мышления, но и об исходном коде в конце.

Интерфейс: Vue.js Element-Ui

Бэкенд: node.js Express fs

идеи

внешний интерфейс
Загрузка большого файла
  • Преобразование больших файлов в формат двоичного потока
  • Используйте атрибуты, которые потоки могут быть разделены, чтобы разделить двоичный поток на несколько копий.
  • Собирайте и разделяйте блоки из равного количества блоков запросов, выполняя запросы параллельно или последовательно.
  • После того, как мы проследим, чтобы все запросы были успешно отправлены, отправляем объединенный сигнал на сервер
http
  • Добавьте другой логотип к каждому блоку резки файла
  • После успешной загрузки запишите идентификатор успешной загрузки.
  • Когда мы приостанавливаем или не можем отправить, мы можем повторно отправить вырезанный файл, который не был успешно загружен.
задняя часть
  • Получите каждый файл резки и после успешного приема сохраните его в указанном месте и сообщите внешнему интерфейсу, что прием прошел успешно.
  • После получения сигнала слияния отсортируйте и объедините все вырезанные файлы, чтобы создать окончательный большой файл, затем удалите вырезанный маленький файл и сообщите переднему интерфейсу адрес большого файла.

Фактически, на данный момент, если вы понимаете и понимаете вышеизложенные идеи, то вы изучили 80% загрузки больших файлов + возобновление точки останова. Следующий конкретный процесс реализации для вас тривиален...

Часть кода загрузки большого файла

В этой html части мы использовали компонент Element-Ui, код прост для понимания.

<el-upload
      drag
      action
      :auto-upload="false" 
      :show-file-list="false" 
      :on-change="changeFile"
      >
      <i class="el-icon-upload"></i>
      <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
</el-upload>

Логика части js, согласно нашему анализу выше, мы можем написать следующую структуру

methods: {
    // 提交文件后触发
    changeFile() {
        this.filepParse()

        // coding... 进行分片
        // ...
        
        // 创建切片请求
        this.createSendQeq()
        this.sendQeq()
        this.mergeUpload
    },
    // 将文件变成二进制,方便后续分片
    filepParse() {
    },
    // 创建切片请求
    createSendQeq() {
    },
    // 将每一个切片 并行/串行 的方式发出
    sendQeq() {
    },
    // 发送代码合并请求
    mergeUpload() {
    }
  }

По вышеприведенному коду написать такую ​​структуру очень просто, дальше нужно доделать логику

Конвертировать файл в бинарный для последующего шардинга

Общие бинарные форматы js включают Blob, ArrayBuffer и Buffer.Здесь мы используем не Blob, который обычно используется в других статьях, а ArrayBuffer.Если вы не знаете о бинарных потоках, пожалуйста, добавьте меня в подписку на мой официальный аккаунт [ Умный и милый Xiaoxuanxuan] Добавьте мой личный WeChat, и мы напишем статью в это время. И поскольку наш процесс синтаксического анализа занимает много времени, мы используем обещания и асинхронную обработку.

filepParse(file, type) {
  const caseType = {
    'base64': 'readAsDataURL',
    'buffer': 'readAsArrayBuffer'
  }
  const fileRead = new FileReader()
  return new Promise(resolve => {
    fileRead[caseType[type]](file)
    fileRead.onload = (res) => {
      resolve(res.target.result)
    }
  })
}
Разделять большие файлы

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

Конечно, когда мы разбиваем большие файлы, нам также нужно учитывать слияние больших файлов, поэтому наши разбиения должны быть регулярными, например, 1-1, 1-2, 1-3, 1-5 и т. д. Когда сервер получает данные среза, и когда получен сигнал слияния, слайсы можно сортировать и объединять.

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

const buffer = await this.filepParse(file,'buffer')
      
const sparkMD5 = new SparkMD5.ArrayBuffer()

sparkMD5.append(buffer)
this.hash = sparkMD5.end()

И когда мы назвали каждый слайс, мы также изменили его на форму хеш-1, хэш-2,

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

const partSize = file.size / 10
let current = 0

 for (let i = 0 ;i < 10 ;i++) {
   let reqItem = {
     chunk: file.slice(current, current + partSize),
     filename: `${this.hash}_${i}.${suffix}`
   }
   current += partSize
   partList.push(reqItem)
 }
 this.partList = partList

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

Создать запрос на срез

Здесь следует отметить, что данные, которые мы отправляем, имеют формат данных FormData, поэтому вы можете найти данные для запросов. Если вы все еще этого не понимаете, старые правила приветствуются, пожалуйста, добавьте меня в подписку на мою общедоступную учетную запись [Smart and Cute Xiaoxuanxuan] и добавьте мой личный WeChat, и мы напишем статью, чтобы объяснить это.

createSendQeq() {
    const reqPartList = []
    this.partList.forEach((item,index) => {
      const reqFn = () => {
        const formData = new FormData();
        formData.append("chunk", item.chunk);
        formData.append("filename", item.filename);
        return axios.post("/upload",formData,{
          headers: {"Content-Type": "multipart/form-data"}
        }).then(res => {
          console.log(res)
        })
      }
      reqPartList.push(reqFn)
    })
    return reqPartList
}
Излучайте каждый срез параллельно/последовательно

Фрагменты теперь разделены, и наш запрос упакован. В настоящее время у нас есть две схемы Parallel/Serial Поскольку сериал легко понять, вот пример серийного номера.

Каждый раз, когда мы успешно отправляем запрос, наш соответствующий индекс увеличивается на единицу, что доказывает, что наша отправка прошла успешно. Когда нижний индекс i совпадает с нашим количеством фрагментов, мы по умолчанию отправляем его успешно, вызывая запрос на слияние.

sendQeq() {
 const reqPartList = this.createSendQeq()
  let i  = 0 
  let send = async () => {
    if (i >= reqPartList.length) { 
      // 上传完成
      this.mergeUpload()
      return 
    }
    await reqPartList[i]()
    i++
    send()
  }
  send()
  
}

Конечно, самым большим недостатком параллельной передачи является то, что она не такая быстрая, как последовательная, но преимущество в том, что код прост и понятен.

Часть кода возобновления точки останова

Поймите предыдущие идеи, в этой части кода также легко получить идеи. Когда вы нажмете «Пауза», остановите загрузку. Если вы нажмете, чтобы продолжить загрузку, мы продолжим загрузку оставшихся запросов, поэтому нам необходимо обработать успешно загруженные запросы.

if (res.data.code === 0) {
    this.count += 1;
    // 传完的切片我们把它移除掉
    this.partList.splice(index, 1);
}

Если загрузка прошла успешно, мы удалим его из массива запросов, чтобы убедиться, что остальная часть будет загружена.

Что касается кода сервера, я не буду здесь его подробно объяснять, пока вы понимаете общую идею, понять код сервера несложно. Если у вас есть какие-либо вопросы, пожалуйста, обратите внимание на общедоступную учетную запись [Smart and Cute Xiaoxuanxuan] и добавьте мою личную WeChat, мы будем учиться и прогрессировать вместе. Конечно, если вы хотите получить код, ответьте на ключевое словоbigFileТолько что...

Далее, давайте снова проанализируем нашу загрузку большого файла.

краткое изложение проблемы

Текущий пример дает простую идею, например:

  • Что делать, если загрузка фрагмента не удалась?
  • Как сделать параллельную загрузку (быстрее)
  • На данный момент указано количество ломтиков, как изменить его на фиксированный размер ломтиков
  • Что делать, если я обновляю страницу во время загрузки?
  • Как справиться с загрузкой больших файлов с помощью Web Work
  • Как реализовать вторую передачу

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

Если вы чувствуете, что эта статья полезна для вас, пожалуйста, обратите внимание на общественное число [Smart and Cute Xiaoxuanxuan]