Сравнение 5-ти методов фронтальной загрузки файлов (дополнительно получить имя файла

JavaScript

предисловие

Скачивание файлов на фронтенд-сайте крайне распространенное требование.Решения разные давно были.Почему вы до сих пор пишете такую ​​старую статью?Просто я недавно привожу нового человека.Когда мы придем к типичная проблема "не могу скачать txt, png и другие файлы", я дам ему краткое описание нескольких способов загрузки. Поделитесь кстати, может кому очень нужно.

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

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

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

Давайте посмотрим, как сгенерировать форму и какая форма генерируется:

/**
 * 下载文件
 * @param {String} path - 请求的地址
 * @param {String} fileName - 文件名
 */
function downloadFile (downloadUrl, fileName) {
    // 创建表单
    const formObj = document.createElement('form');
    formObj.action = downloadUrl;
    formObj.method = 'get';
    formObj.style.display = 'none';
    // 创建input,主要是起传参作用
    const formItem = document.createElement('input');
    formItem.value = fileName; // 传参的值
    formItem.name = 'fileName'; // 传参的字段名
    // 插入到网页中
    formObj.appendChild(formItem);
    document.body.appendChild(formObj);
    formObj.submit(); // 发送请求
    document.body.removeChild(formObj); // 发送完清除掉
}

преимущество

  • Традиционный метод, хорошая совместимость, отсутствие проблем с ограничением длины URL

недостаток

  • Невозможно узнать ход загрузки
  • Невозможно напрямую загрузить типы файлов, которые браузеры могут просматривать напрямую (например, txt/png и т. д.)

открыть или location.href

На самом деле самый простой и прямой способ — следоватьaОтметьте доступ к ссылкам для скачивания, например

window.open('downloadFile.zip');

location.href = 'downloadFile.zip';

Конечно, адрес также может быть адресом интерфейса API, а не просто адресом ссылки.

преимущество

  • Просто и удобно

недостаток

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

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

мы знаем,aТег может получить доступ к адресу загруженного файла, а браузер помогает скачать. Однако для таких файлов, как txt, png, jpg и gif, которые браузеры поддерживают прямой просмотр, прямая загрузка недоступна (вы можете щелкнуть правой кнопкой мыши и сохранить как из меню).

Чтобы решить эту проблему прямого просмотра без загрузки, вы можете использоватьdownloadАтрибуты.

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

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

  • Edge 13 аварийно завершает работу при попытке загрузить URL-адреса данных.
  • Chrome 65 и более поздние версии поддерживают только ссылки для скачивания того же происхождения.
  • Firefox поддерживает только ссылки для скачивания того же происхождения.

Исходя из приведенного выше описания, если вы попытаетесь загрузить междоменную ссылку, тоdownloadЭффект пропадет, если не установитьdownloadСтабильная производительность. То есть то, что браузер может предварительно просмотреть, все равно будет предварительно просмотрено, а не загружено.

Простое использование:

<a href="example.jpg" download>点击下载</a>

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

<a href="example.jpg" download="test">点击下载</a>

Как и выше, файл с именемtestкартинка

Следите за тем, поддерживается ли загрузка

Чтобы узнать, поддерживает ли браузерdownloadАтрибуты можно отличить по простому предложению кода

const isSupport = 'download' in document.createElement('a');

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

преимущество

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

недостаток

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

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

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

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

  • IE10 и ниже не поддерживаются.
  • Доступ в браузере SafariBlob UrlилиObject URLв настоящее время имеет недостатки, как описано ниже черезURL.createObjectURLсгенерированная ссылка.caniuseНа официальном сайте указано

Safari has a serious issue with blobs that are of the type application/octet-stream

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

/**
 * 下载文件
 * @param {String} path - 下载地址/下载请求地址。
 * @param {String} name - 下载文件的名字/重命名(考虑到兼容性问题,最好加上后缀名)
 */
downloadFile (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) {
            // 如果是IE10及以上,不支持download属性,采用msSaveOrOpenBlob方法,但是IE10以下也不支持msSaveOrOpenBlob
            if ('msSaveOrOpenBlob' in navigator) {
                navigator.msSaveOrOpenBlob(this.response, name);
                return;
            }
            // 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);
        }
    };
}

Этот метод не может быть упущенaпомеченdownloadУстановка свойств. Поскольку запрос передачи был установлен для типа возврата данныхBlobтип(xhr.responseType = 'blob'),такtarget.responseтолько одинBlobобъект, вы увидите два свойства, когда распечатаете егоsizeиtype. Несмотря на то чтоtypeСвойство уже указывает тип файла, но на всякий случайdownloadВ значении свойства указывается имя запроса, например Firefox не указывает загруженные файлы, не распознает тип.

Вы можете заметить, что в приведенном выше коде есть два комментария, на самом деле, помимо описанного выше метода записи, есть еще один метод записи, который немного изменился. Если не установлено при отправке запросаxhr.responseType = 'blob', запрос ajax по умолчанию вернетDOMStringтип данных, то есть строка. В настоящее время необходимы два прокомментированных кода, и возвращаемый текст преобразуется вBlobобъект, а затем создайте URL-адрес большого двоичного объекта, вам нужно закомментировать исходныйconst url = URL.createObjectURL(target.response).

преимущество

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

недостаток

  • Проблемы совместимости, недоступные ниже IE10; браузер Safari может обратить внимание на использование

Использовать base64

Использование здесь такое же, как и вышеBlobВ основном та же идея, единственное отличие состоит в том, что выше используетсяBlobгенерация объектаBlob URL, и здесь генерируетсяData URL, так называемыйData URL,этоbase64Закодированная форма URL.

/**
 * 下载文件
 * @param {String} path - 下载地址/下载请求地址。
 * @param {String} name - 下载文件的名字(考虑到兼容性问题,最好加上后缀名)
 */
downloadFile (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 fileReader = new FileReader();
            fileReader.readAsDataURL(this.response);
            fileReader.onload = function () {
                const a = document.createElement('a');
                a.style.display = 'none';
                a.href = this.result;
                a.download = name;
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
            };
        }
    };
}

преимущество

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

недостаток

  • Проблемы совместимости, недоступные ниже IE10

Об именах файлов

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

Content-Disposition

При возврате файлового потока мы наблюдаем информацию, возвращаемую интерфейсом в браузере, и увидим такой заголовок:Content-Disposition

Content-Disposition: attachment; filename=CMCoWork__________20200323151823_190342.xlsx; filename*=UTF-8''CMCoWork_%E4

Приведенные выше значения являются примерами.

Он содержит имя файла, мы можем найти способ получить в нем имя файла. Мы видим, что естьfilename=иfilename*=, последний не обязательно имеет, в старых браузерах или отдельных браузерах эта форма не будет поддерживаться,filename*УсыновленныйRFC 5987Метод кодирования, указанный в .

Итак, если вы хотите получить имя файла, это делается, перехватите два значения поля в этой строке.

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

  • filename, серверной части необходимо обработать форму кодирования, но даже если серверная часть обрабатывается, ситуация синтаксического анализа будет отличаться в зависимости от каждого браузера. С ним трудно иметь дело, поэтому он позадиfilename*
  • filename*, поддерживается современными браузерами, чтобы решитьfilenameнедостатки, как правилоUTF-8,мы используемdecodeURIComponentЕго можно расшифровать и восстановить первоначальный вид. Конечно, перед декодированием вы вводите значениеUTF-8''Эта часть удалена.

Итак, прежде чем мы это осознаем, нам нужно понять, что принятиеContent-DispositionСодержимое не на 100% соответствует вашим ожиданиям, если только ваше имя файла не состоит из английских цифр.

Мы извлекаем значение имени файла:

// xhr是XMLHttpRequest对象
const content = xhr.getResponseHeader('content-disposition'); // 注意是全小写,自定义的header也是全小写
if (content) {
    let name1 = content.match(/filename=(.*);/)[1]; // 获取filename的值
    let name2 = content.match(/filename\*=(.*)/)[1]; // 获取filename*的值
    name1 = decodeURIComponent(name1);
    name2 = decodeURIComponent(name2.substring(6)); // 这个下标6就是UTF-8''
}

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

дефект

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

настраиваемый заголовок

По сути то же, что и вышеContent-DispositionПочти, но здесь мы не используем заголовок по умолчанию, мы настраиваем его сами.response header, решить метод кодирования с помощью серверной части и вернуться, клиентская часть напрямую получает этот настраиваемый заголовок, а затем использует соответствующее декодирование, например, с помощьюdecodeURIComponent.

Но всем нам нужно знать, что в случае кроссдоменности заголовок, полученный внешним интерфейсом, имеет только 6 базовых полей по умолчанию:Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma.

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

Access-Control-Expose-Headers: Content-Disposition, custom-header

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

Переименовать

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

function findType (name) {
    const index = name.lastIndexOf('.');
    return name.substring(index + 1);
}

Пожалуйста, не воспроизводите без разрешения

Мой публичный аккаунт WeChat