Как загружать файлы и устанавливать тип контента

axios

предисловие

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

Пример разбора, как загрузить картинку

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

Форма

используется для сбора данных, отправленных пользователем, и отправки их на сервер. Следующий код содержит: поле выбора файла (получить локальный файл), кнопку отправки (управление формой отправки).
<form action="http://localhost:8899/react/aa" method="post" enctype="multipart/form-data">
    <input type="file" name="image"  multiple="multiple" />
    <input type="submit" value="提交"/>
</form>

Когда пользователь нажимает кнопку «Отправить», каждый элемент управления создает пару «ключ-значение», имя ключа — это атрибут имени элемента управления, а значение ключа — атрибут значения элемента управления. Мы используем node в качестве сервера и используем koa-body для разбора файлов, доставленных по почте.

// 服务端获取请求中的文件
router.post('/aa',  async ctx => {
  console.log(ctx.request.files);
})

использовать запрос axios

Данные формы отправляются на сервер в виде пар ключ-значение, что автоматически выполняется браузером. Но иногда мы хотим завершить процесс с помощью сценариев, построения и редактирования пар ключ-значение формы. Браузер изначально предоставляетFormData 对象для завершения этой работы.

new FormData(form)

let formdata = new FormData(form);

Параметр конструктора FormData() является элементом формы, который является необязательным. Если параметр опущен,就表示一个空的表单, иначе будет обработана пара ключ-значение внутри элемента формы. Как правило, метод, который мы используем, заключается в создании пустого объекта формы. FormData предоставляет множество методов экземпляра. Мы можем добавлять пары ключ-значение в форму с помощью метода append.

formdata.append(key1,value1)
formdata.append(key2,value2)

Следующий код реализует загрузку файла путем отправки данных формы formdata через axios.

 <input type="file" @change="onChange">
    methods:{
        onChange(event){
            const params = new FormData()
            params.append('file',event.target.files[0])
             axios.post('http://localhost:8899/react/aa',params,{
              headers:{
                'content-type':'multipart/form-data'
              }
            })
        }
    }

просмотреноaxios 源码Обнаружено, что для загрузки файлов не нужно устанавливать исходный код типа контента.lib/adapters/xhr.jsФайл определяет браузер для использования XHR:

module.exports = function xhrAdapter(config) {
  return new Promise(function dispatchXhrRequest(resolve, reject) {
        // config 是传入的配置对象 如: {url,method,data,headers}
        // 获取传入的参数和请求头
        var requestData = config.data;
        var requestHeaders = config.headers;
        // 判断是否为 formData 实例,如果是删除 请求头中的 content-type
        if (utils.isFormData(requestData)) {
          delete requestHeaders['Content-Type']; // Let the browser set it
        }
  })
}

Реализация isFormData: FormData — это конструктор объекта формы.Используйте instanceof, чтобы определить, появляется ли свойство прототипа конструктора в цепочке прототипов объекта экземпляра.

/**
 * Determine if a value is a FormData
 *
 * @param {Object} val The value to test
 * @returns {boolean} True if value is an FormData, otherwise false
 */
function isFormData(val) {
  return (typeof FormData !== 'undefined') && (val instanceof FormData);
}

Установить значение по умолчанию для типа контента

lib/defaults.jsдокумент

defaults.headers = {
  common: {
    'Accept': 'application/json, text/plain, */*'
  }
};

utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {
  defaults.headers[method] = {};
});

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);
});

Определите тип входящих данных, чтобы установить необоснованный тип контента

При использовании axios для инициирования запроса он изменит запрошенные данные перед их отправкой на сервер с помощью метода transformRequest по умолчанию,'PUT', 'POST', 'PATCH' and 'DELETE'Допустимо только для этих методов запроса.

 // lib/defaults.js 文件
 transformRequest: [function transformRequest(data, headers) {
    normalizeHeaderName(headers, 'Accept');
    normalizeHeaderName(headers, 'Content-Type');
    if (utils.isFormData(data) ||
      utils.isArrayBuffer(data) ||
      utils.isBuffer(data) ||
      utils.isStream(data) ||
      utils.isFile(data) ||
      utils.isBlob(data)
    ) {
      return data;
    }
    if (utils.isArrayBufferView(data)) {
      return data.buffer;
    }
    if (utils.isURLSearchParams(data)) {
      setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
      return data.toString();
    }
    if (utils.isObject(data)) {
      setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
      return JSON.stringify(data);
    }
    return data;
  }],

Таким образом, в целом, при использовании запроса axios не нужно устанавливать тип содержимого.Если необходимо обработать какие-то особые случаи, вы можете поместить его в transformRequest:[function(data,headers){return data}] для обработки

Использовать запрос на получение

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

fetch(api,{
    url,
    headers:{
        'content-type':'multipart/form-data'
    },
    mode:'no-cors'
})

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

500 (Internal Server Error) Это ошибка, о которой сообщает API-сервер запросов,Unexpected end of inputЭто ошибка, о которой сообщает браузер, выполняющий response.json().Почему браузер сообщает о неожиданном окончании вводаО ней я расскажу отдельно позже, а потом рассмотрим причины ошибки на стороне сервера.

Содержимое загруженного файла должно быть отмечено границей. Правильный Content-Type: mutlipart/form-data; border = -----xxxx. Проверьте наши текущие загруженные запросы:

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

При использовании запроса на выборку установка Content-type приведет к потере граничного параметра, поэтому在上传文件时, нет необходимости задавать поле заголовков, браузер автоматически сгенерирует полный тип контента (включая границы).

После удаления поля заголовков API выборки может нормально загружать файлы! !

пример кода

пример кода

Неожиданный конец ошибки ввода

На самом деле это синтаксическая ошибка js.В следующем коде fetch api используется для получения ресурсов и возвращает объект ответа ответа.Если мы укажем тип содержимого какapplication/jsonЗатем сервер вернет нам строку в формате json, поэтому, когда мы вызовем resopnse.json(), мы сможем разобрать правильный объект.

fetch(api).then(response=>response.json()).then(res=>res)

Вы можете понимать resopnse.json() как JSON.parse(), поэтому вы можете использовать JSON.parse() для имитации предыдущегоUnexpected end of inputсообщить об ошибке浏览器在读取我们的代码时,碰到了不可预知的错误,导致浏览器 无语进行下面的读取Следующий код выведет эту ошибку.

JSON.parse("{")
JSON.parse('[{"test": 4}')

Есть также общиеUnexpected token < in JSON at position 0Продолжайте имитировать сцену, в которой возникает ошибка, а внешний интерфейс продолжает использоватьresponse.jsonДля анализа данных, возвращаемых сервером. Если сервер не вернет предопределенное значение типа контента, он получит эту ошибку.

// 服务端
router.post('/aa',  async ctx => {
  ctx.body = '<div>内容</div>'
})

Если вам нужна простая симуляция, используйте ее напрямуюJSON.parse("<h1>1</h1>")получит тот же результат. Код ниже тот же.

JSON.parse("{sd}")
// Unexpected token s in JSON at position 1
JSON.parse("d}")
// Unexpected token d in JSON at position 0

Типы, поддерживаемые JSON.parse(), следующие:

JSON.parse('{}');              // {}
JSON.parse('true');            // true
JSON.parse('"foo"');           // "foo"
JSON.parse('[1, 5, "false"]'); // [1, 5, "false"]
JSON.parse('null');            // null
JSON.parse('1');               //  1

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

Организовать тип контента

axios

получить метод запроса

Если метод запроса получен, будет использоваться только метод кодирования application/x-www-form-urlencoded.

axios.get('/user',{
              params:{
                id:1,
                name:'dd',
                person:'张三'
              }
            })

теоретически запросhttp://localhost:8080/user?id=1&name=dd&person=张三Но для специальных символов это будет кодировка URL.
Кодирование URL-адресов также широко известно как процентное кодирование (кодирование URL-адресов, также известное как процентное кодирование), потому что его метод кодирования очень прост: использование знака процента и двух символов — 0123456789ABCDEF — представляет собой шестнадцатеричную форму байта. Набор символов по умолчанию, используемый кодировкой URL, — US-ASCII. Например, соответствующий байт a в коде US-ASCII — 0x61, тогда кодировка URL — %61.
Итак, после кодирования фактический путь запроса становитсяhttp://localhost:8080/user?id=1&name=dd&person=%E5%BC%A0%E4%B8%89

метод почтового запроса

  1. application/x-www-form-urlencoded формат
    Этот метод используется по умолчанию, при использовании этого метода входящие параметры пары должны быть преобразованы вname=hehe&age=10Формат, введенный, если пара, которую вы передаете, является объектом, конфигурация axios по умолчанию установит тип содержимого в application/json;charset=utf-8

	const service = axios.create({
      baseURL,
      headers
    })
    // 判断是否使用 'application/x-www-form-urlencoded' content-type
    if (isFormUrlencoded) {
      let list: string[] = []
      for (let key in params) {
        list.push(`${key}=${encodeURIComponent(params[key])}`)
      }
      fetchResult = service({
        url,
        method: 'POST',
        // 将 {a:1,b=2} 处理成 a=1&b=2 的格式,并且将他传递给 data 字段
        data: list.join('&')
      })
    } else {
      fetchResult = service.post(url, params)
    }
  1. text/plain
    axios.post('http://localhost:8899/react/aa','我就是内容',{
              headers:{
                 'content-type':'text/plain'
              }
            })
    

3. multipart/form-data При загрузке файлов не нужно задавать этот тип, браузер автоматически его добавит!!!
4. application/json axios.post('http://localhost:8899/react/aa',{name:'dd',age:18})

fetch

получить кодировку метода application/x-www-form-urlencoded

В путь необходимо добавить способ передачи параметров для получения, поэтому работу по кодировке URL нужно реализовать вручную

// 在URL中写上传递的参数
fetch('http://localhost:8080?a=1&b=2', { 
    method: 'GET'
  })
  
 // 处理传入的 params 参数
    for(let key in params){
      param += `${key}=${encodeURIComponent(params[key])}&`
    }

почтовый путь

  1. application/x-www-form-urlencoded
fetch('http://localhost:8080',{
    method:'POST',
    headers:{
        'content-type':'application/x-www-form-urlencoded'
    },
    mode:'no-cors',
    body:'name=dd&age=12'
})
  1. application/json

Вам нужно использовать JSON.stringify() для преобразования объекта в строку JSON с телом в качестве поля, которое принимает данные.

fetch(url, {
  method: 'POST', // or 'PUT'
  body: JSON.stringify(data), 
  headers: new Headers({
    'Content-Type': 'application/json'
  })
})
  1. multipart/form-data При загрузке файлов не нужно задавать этот тип, браузер автоматически его добавит!!!
let data = new FormData()
data.append('file',target.files[0])
fetch(url,{
    body:data,
    method:"POST",
    headers:{}
})

Ссылаться на

axios
Использование объекта FormData
Error when POST file multipart/form-data
Chrome: Uncaught SyntaxError: Unexpected end of input
Заметки по веб-разработке Кодирование и декодирование URL-адресов скриншот заголовка запроса