Фронтальная загрузка файлов и автоматический анализ браузера

внешний интерфейс
Фронтальная загрузка файлов и автоматический анализ браузера

В этой статье рассказывается о некоторых знаниях, связанных с загрузкой файлов через интерфейс.

Когда дело доходит до загрузки файлов на переднем конце, первое, о чем я думаю, это то, что когда я учился в школе, я сам построил среду nginx + php, а затем открыл страницуhttp://localhost:80/index.php, Но странно обнаружить, что каждый раз, когда вы его открываете, он становится загрузкой файла.

index.php

Позже я узнал, что заголовок запроса будет содержатьAcceptполе, в заголовке ответа будетContent-Typeполе, первое используется, чтобы сказатьSКакие типы контента может принять клиент, рассказывает последнийCКакой тип контента возвращается с терминала.

MIME

MIME— это стандартизированный способ представления характера и формата документа, и браузеры обычно используют MIME для определения типа (а не расширения файла).

Content-type использует типы MIME, соответствующие файлам jpg.image/jpeg, файл js соответствуетapplication/javascript, xlsx этоapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet.

MIME имеет два типа по умолчанию:

  • text/plainПредставляет значение по умолчанию для текстовых файлов. Текстовый файл должен быть удобочитаемым и не содержать двоичных данных.
  • application/octet-streamУказывает значение по умолчанию для всех остальных случаев. Неизвестный тип файла должен использовать этот тип.

Полный список типов MIME

👆index.phpПричина, по которой это станет загрузкой файла, заключается в том, что я неправильно разобрал файл php из-за ошибки установки, а nginx напрямую обращается к файлу и добавляет contentType по умолчаниюapplication/octet-stream. Потому что Chrome не может выполнитьapplication/octet-streamОтформатируйте файлы, операция по умолчанию заключается в их загрузке (разные браузеры выполняют разные операции с файлами, которые не могут быть обработаны, и некоторые браузеры будут пытаться обнюхать).

Это также объясняет, почему мы напрямую обращаемся кhttps://xxx/foo/bar.zipПри ожидании ресурса браузер загружает его напрямую.

Вставьте класс безопасности:

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

evil

На самом деле это файл html, но суффикс написан как jpeg для загрузки. В это время, если сервер не устанавливает contentType, он напрямую считывает файл и возвращает его во внешний интерфейс.

# koa router 演示代码
router.get('/assets/:file.jpeg', (ctx) => {
  ctx.body = fs.createReadStream(`./public/assets/${ctx.params.file}.jpeg`);
});

Браузер из лучших побуждений получает MIME-тип как application/octet-stream, а затем читает содержимое и находит, а, это html, мы должны показать его напрямую. 🌚🌚🌚

evil

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

Чтобы избежать подобных инцидентов с безопасностью, установите

  • Добавьте соответствующий тип содержимого к возвращаемому содержимому.
  • добавить заголовки ответаX-Content-Type-Options: nosniff, чтобы браузер не пытался нюхать
router.get('/assets/:file.jpeg', (ctx) => {
  ctx.type = 'image/jpeg';
  ctx.set('X-Content-Type-Options', 'nosniff');
  ctx.body = fs.createReadStream(`./public/assets/${ctx.params.file}.jpeg`);
});

Только в демонстрационных целях koa следует использовать для предоставления услуг статических ресурсов.koa-staticи другие пакеты с открытым исходным кодом, они автоматически добавят contentType.

Как заставить браузер загружать изображения

Вышеупомянутые типы документов, которые не поддерживаются соответствующим браузером, будут загружены по умолчанию. А как насчет тех типов, которые можно обрабатывать? Такие как картинки, js, json и другой контент?

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

json

Что, если требуется разрешить пользователям загружать файлы json?

Есть еще один поле заголовка ответаConten-disposition👹, ответ Content-Disposition на содержимое указанной формы, которая связана с формой (которая является частью веб-страницы или страницы), или загрузите и сохраните ее на локальном компьютере в качестве вложения, соответственно.inlineа такжеattachment.

Content-Disposition: inline
Content-Disposition: attachment

В режиме вложения вы также можете указать имя файла и расширение загружаемого файла.

Content-Disposition: attachment; filename="filename.jpg"

Образец кода:

router.get('/hello.json', (ctx) => {
  ctx.type = 'application/json';
  ctx.set('Content-Disposition', 'attachment; filename="hello.json"');
  // 上面两行代码,可以简写成 ctx.attachment('hello.json');
  ctx.body = {
    hello: 'world',
  };
});

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

export

Атрибут загрузки HTML

Существует также способ позволить браузеру сохранить файл локально. Он добавляется HTML5 биркойdownloadАтрибуты.

<a href="/images/xxx.jpg" download="panda.jpg" >My Panda</a>

Когда пользователь нажимает на метку, загружается файл, указанный в href, иdownloadЗначение атрибута соответствует имени загружаемого файла. Более гибкий способ — инкапсулировать его в метод, динамически создать ссылку, инициировать щелчок, чтобы загрузить ее напрямую и сохранить как.

<script>
function downloadAs (url, fileName) {
  const link = document.createElement('a');
  link.href = url;
  link.download = fileName;
  link.target = '_blank'

  document.body.appendChild(link);
  link.click();
  link.remove();
}

downloadAs('http://localhost:3001/hello.json', 'world.json');
</script>

Инициируйте асинхронное получение ресурсов, а затем загрузите

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

С помощью атрибута загрузки в сочетании с Blob, Url.createObjectURL(), внешний интерфейс может асинхронно запрашивать ресурсы и экспортировать файлы.

const xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost:3001/pack.zip');
xhr.responseType = 'blob';

xhr.onload = function () {
  const blob = xhr.response;
  const url = URL.createObjectURL(blob);
  downloadAs(url, 'mypack.zip');
  URL.revokeObjectURL(url);
};
xhr.send();

настраиватьxhr.responseType = 'blob'затем, когда запрос завершается нормальноxhr.responseПолучается объект Blob, URL.createObjectURL(Blob), и получается ссылка на большой двоичный объект, например:blob:http://localhost:3001/11a01a60-e10c-4515-825f-fb4a4219b33b. Затем вы можете напрямую установить href для тега a как обычный URL-адрес.

async-download

BlobObject представляет собой неизменяемый, примитивный объект, похожий на файл данных. Объект File также расширяется на его основе и временно понимается как абстрактный файловый объект.

Передача URL.createObjectURL создает URL-адрес, который ссылается на объект Blob или File. Жизненный цикл этого URL-адреса привязан к окну, и его следует вызывать при исчерпании утечек памяти.URL.revokeObjectURL()освобожден.

Большой двоичный объект может принимать данные собственного типа Javascript в качестве параметров, например чистые данные имитации внешнего интерфейса, и экспортировать их в виде CSV-файла.

const rows = [
  ["id", "firstname", "lastname"],
  ["1", "foo", "foo"],
  ["2", "bar", "baz"],
];

const data = rows.reduce(function(cur, next) {
  return cur + next.join(',') + '\n';
}, '');
const blob = new Blob([data]);
const url = URL.createObjectURL(blob);
downloadAs(url, 'mock.csv');

совместимость

Совместимость атрибута загрузки невысокая, на данный момент всего 80%. можно использовать напрямуюFileSaver.jsВыполните резервную обработку.

download

Расширенное чтение

Подавать жалобы

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

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