Расскажите о загрузке и скачивании файлов

Java внешний интерфейс
Расскажите о загрузке и скачивании файлов

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

форма отправки формы

Самый традиционный метод загрузки файлов — использоватьformДля загрузки файлов из формы нужно только поставитьenctypeУстановить какmultipart/form-data.这种方式上传文件不需要 js ,而且没有兼容问题,所有浏览器都支持,就是体验很差,导致页面刷新,页面其他数据丢失。

<form method="post" action="xxxxx" enctype="multipart/form-data">
  选择文件:<input type="file" name="file" />
  <br />
  标题:<input type="text" name="title" />
  <br />
  <button type="submit">提交</button>
</form>

Уведомление:inputдолжен быть установленnameатрибут, иначе данные не могут быть отправлены

загрузка файлового интерфейса

Этот метод предоставляет серверу интерфейс, устанавливает соответствующий заголовок запроса и отправляет внешний интерфейс.formDataформа файловых данных.

<input id="uploadFile" type="file" name="file" accept="image/png,image/gif" />
  • accept: указывает тип MIME файла, который можно выбрать, а несколько типов MIME разделяются английскими запятыми.
  • multiple: Можно ли выбрать несколько файлов
$('#uploadFile').on('change', function (e) {
  var file = this.files[0]

  var formData = new FormData()
  formData.append('file', file)

  $.ajax({
    url: 'xxxx',
    type: 'post',
    data: formData,
    cache: false,
    contentType: false,
    processData: false,
    success: function (res) {
      //
    },
  })
})
  • processDataУстановите значение «ложь». потому что значение данныхFormDataобъект, обработка данных не требуется.
  • cacheУстановите значение false, чтобы загружать файлы без кэширования.
  • contentTypeУстановите значение «ложь».

Частичная загрузка

Иногда файлы, которые мы загружаем, могут быть очень большими, например, видео и т. д., которые могут достигать 2 ГБ, что приводит к слишком низкой скорости загрузки, а иногда даже может произойти тайм-аут ссылки. А иногда сервер устанавливает размер файла, который разрешено загружать, а слишком большой файл загружать нельзя. Чтобы решить эту проблему, мы можем загружать файл частями, каждый раз загружая только небольшую часть, например 1M.

идеи

  1. Разрежьте файл на небольшую часть в соответствии с определенным размером (например, 1M) и прикрепите хеш-значение к фрагменту для идентификации.
  2. Одновременно отправьте каждый файл среза на сервер, и сервер сохранит информацию о каждом файле среза.
  3. После завершения загрузки слайсов сервер объединяет их в соответствии с идентификатором файла и удаляет файлы слайсов после слияния.

Это может эффективно сократить время загрузки, поскольку каждый фрагмент загружается одновременно. Конкретные этапы реализации описаны ниже. (PS: это метод реализации нашей компании, а не единственный метод, и код, связанный с конкретными интерфейсами, здесь размещаться не будет)

Сгенерировать хеш-значение

Будь то загрузка информации о файле или загрузка файлов фрагментов, файлы и фрагменты должны быть сгенерированы.hash. простейшийhashЗначение можно определить по имени файла + подстрочному индексу, но как только имя файла будет изменено, оно потеряет свою силу.Фактически, пока содержимое файла остается неизменным,hashне должен меняться, поэтому правильный способ - генерировать на основе содержимого файлаhash. Наша компания используетspark-md5Библиотека, я не буду вдаваться в подробности здесь.

Загрузка информации о файле

Перед загрузкой сегментов файла необходимо сохранить информацию обо всем файле, такую ​​как общий размер файла, имя файла, значение хеш-функции и т. д. Основная цель — инициализировать событие загрузки сегмента файла и вернуть идентификатор файла для каждого сегмента Представление фильма.

getFileId (file) {
  let vm = this
  let formData = new FormData()
  formData.append('file', file)
  axios({
    timeout: 5 * 60 * 1000,
    headers: {
      'Content-Type': 'application/json-',
      'x-data': JSON.stringify({
        fileName: file.fileName,
        size: file.size,
        hash: 'hashxxx',
      }),
    },
    url: 'xxxxxx',
    method: 'POST',
  })
  .then((res) => {
    if (res.code === '200') {
      return res.data.fileId
    })
  .catch((err) => {
    console.log(err)
  })
}

нарезка файлов

Когда внешний интерфейс получит локальный образ, используйтеBlob.prototype.sliceметод (и массивsliceМетод аналогичен), нарезаем большой файл по 1М без мелких кусочков, возвращаем определенный срез исходного файла, а затем параллельно загружаем каждый слайс на сервер.

getCkunk (file, fileId) {
  let vm = this
  let chunkSize = 1024 * 1024
  let totalSize = file.size
  let count = Math.ceil(totalSize / chunkSize)
  let chunkArr = []
  for (let i = 0; i < count; i++) {
    if (i === count.length - 1) {
      chunkArr.push(file.slice(i * chunkSize, totalSize))
    } else {
      chunkArr.push(file.slice(i * chunkSize, (i + 1) * chunkSize))
    }

  for (let index = 0; index < count; index++) {
    let item = chunkArr[index]
    this.uploadChunk(item, index, fileId)
  }
}

Способ загрузки каждого фрагмента на сервер. Метод получения хэш-значения здесь опущен.

 ploadChunk(item, index, fileId) {
   let formData = new FormData()
   formData.append('file', item)
   request({
     headers: {
       'Content-Type': 'application/octet-stream;',
       'x-data': JSON.stringify({
         fileId: fileId,
         partId: index + 1,
         hash: res,
       })
     },
     url: 'xxxxx',
     method: 'POST',
     data: formData,
   })
   .then((res) => {
     return res.data.path
   })
   .catch((err) => {
     console.log(err)
   })
 }

Показать индикатор загрузки

Из-за относительно большого размера файла даже загрузка из нескольких частей занимает определенное время. В это время серверной части необходимо добавить загруженное поле 100% к результату воспроизведения каждого сегмента. Когда внешний интерфейс получает возвращаемое значение, он изменяет текущий прогресс.

Когда загрузка последнего фрагмента завершена, сервер возвращает URL-адрес файла, внешний интерфейс получает URL-адрес и меняет статус индикатора выполнения на 100%.

http

Вышеупомянутая многокомпонентная загрузка устраняет тайм-аут загрузки больших файлов и ограничения сервера. Но для больших файлов загрузка не завершается за короткий промежуток времени, а иногда даже сталкивается с отключением или ручной приостановкой. Мы должны повторно загружать весь файл? Конечно, мы этого не хотим. Здесь пригодится резюме точки останова.

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

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

загрузка документа

Есть несколько способов загрузки файлов

форма отправки формы

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

function downloadFile(downloadUrl, fileName) {
  // 创建表单
  let form = document.createElement('form')
  form.method = 'get'
  form.action = downloadUrl
  //form.target = '_blank';	// form新开页面
  document.body.appendChild(form)
  form.submit()
  document.body.removeChild(form)
}
  • Преимущества: хорошая совместимость, отсутствие проблем с ограничением длины URL.
  • Недостатки: невозможно узнать ход загрузки, невозможно напрямую загрузить типы файлов, которые браузеры могут просматривать напрямую (например, txt/png и т. д.).

window.location.href или Window.Open

Самый простой и прямой способ на самом деле такой же, как тег a для доступа к ссылке для скачивания.

window.open('downloadFile.zip')
location.href = 'downloadFile.zip'

недостаток

  • Будет проблема с ограничением длины URL
  • Необходимо обратить внимание на проблемы с кодировкой URL
  • Типы файлов, которые браузер может просматривать напрямую, недоступны для загрузки, например txt, png, jpg, gif и т. д.
  • Заголовок не может быть добавлен, и аутентификация не может быть выполнена
  • Вы не можете знать ход загрузки

атрибут загрузки тега

Атрибут загрузки — это новый атрибут в HTML5, вы можете понять совместимость нижеcan i use download.

<a href="xxxx" download>点击下载</a>
<!-- 重命名下载文件 -->
<a href="xxxx" download="test">点击下载</a>

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

недостаток

  • Знать адрес загружаемого файла
  • Невозможно загрузить файлы, доступные для просмотра в браузере, в междоменном домене
  • Имеет проблемы с совместимостью, особенно IE
  • Не удается пройти аутентификацию

Использование объектов BLOB-объектов

Помимо использования известного пути к адресу файла для загрузки, этот метод также может загружать файловый поток, отправляя API-запрос ajax. С помощью объекта Blob можно преобразовать файловый поток в двоичный объект Blob.

Идея загрузки очень проста: отправьте запрос на получение двоичных данных, преобразуйте их в объект Blob, используйте URL.createObjectUrl для генерации URL-адреса, присвойте его атрибуту href тега a и загрузите его с помощью скачать.

downdFile (path, name) {
  const xhr = new XMLHttpRequest();
  xhr.open('get', path);
  xhr.responseType = 'blob';
  xhr.send();
  xhr.onload = function () {
    if (this.status === 200 || this.status === 304) {
      // const blob = new Blob([this.response], { type: xhr.getResponseHeader('Content-Type') });
      // const url = URL.createObjectURL(blob);
      const url = URL.createObjectURL(this.response);
      const a = document.createElement('a');
      a.style.display = 'none';
      a.href = url;
      a.download = name;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
    }
  }
}

рекомендуемая статья