怎么修改服务器上文件格式,如何上传文件及content-type的设置

前言

在使用 fetch / axios 时常常会涉及到文件上传,以及其他请求,其中包括一些 content-type ,被这些不同类型到 content-type 搞得头大,到底什么时候该用怎么样的类型呢,本文将会梳理这些问题。

实例分析,如何上传一张图片

表单提交方式

表单

用来收集用户提交的数据,发送到服务器。下面代码中包含:文件选择框(获取本地文件),提交按钮(提交表单控件)。

复制代码

用户点击“提交”按钮,每一个控件都会生成一个键值对,键名是控件的name属性,键值是控件的value属性。我们采用node作为服务端,使用 koa-body 解析 post 方式传递的文件

// 服务端获取请求中的文件

router.post('/aa', async ctx => {

console.log(ctx.request.files);

})

复制代码

dfb26a21118a796d4de0ef403f641a2d.png

使用 axios 请求

表单数据以键值对的形式向服务器发送,这个过程是浏览器自动完成的。但是有时候,我们希望通过脚本完成过程,构造和编辑表单键值对。浏览器原生提供了 FormData 对象 来完成这项工作。

new FormData(form)

let formdata = new FormData(form);

复制代码

FormData()构造函数的参数是一个表单元素,这个参数是可选的。如果省略参数,就表示一个空的表单,否则就会处理表单元素里面的键值对。一般我们使用的方法就是构建一个空的表单对象,FormData 提供很多实例方法,我们可以通过 append 方法来添加表单中的键值对。

formdata.append(key1,value1)

formdata.append(key2,value2)

复制代码

下面的代码通过 axios 提交 formdata 表单数据来实现文件上传

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 源码 发现其实上传文件不需要设置 content-type 源码 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 来检测构造函数的 prototype 属性是否出现在实例对象的原型链上。

/**

* 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);

}

复制代码

给content-type 设置默认值

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);

});

复制代码

a79dc758eeca54f6b8ccdc7597d97c94.png

判断传入数据的类型来设置不通的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 请求不需要设置 content-type 如果有些特殊情况需要处理的可以放在 transformRequest:[function(data,headers){return data}] 中做处理

使用 fetch 请求

为了展示清楚,直接简化fetch请求的封装,具体可以查看下面的 codepen 示例,这里主要是为了展示使用 fetch 上传文件时,同时设置了 headers 请求时会出现什么问题,将代码设置如下:

fetch(api,{

url,

headers:{

'content-type':'multipart/form-data'

},

mode:'no-cors'

})

复制代码

fa3b1d35cabacb66426c001fc8fce15e.png

当我们发起图片上传请求时,得到两处报错,在报错右处有错误产生的文件地方,点击这个文件可以看到

f7586bdb1409e1a4e7013db6ae5aad5d.png

500 (Internal Server Error) 是请求api服务端报的错,Unexpected end of input 是浏览器运行 response.json()报的错。为什么浏览器会报 Unexpected end of input 后面我会单独讲,接下来我们看下服务端的报错原因。

45be628079e3956a137cdc05ea301fdb.png

上传的文件内容是需要通过 boundary 来标明分割线的,正确的Content-Type: mutlipart/form-data; boundary = -----xxxx这种形式。

查看我们当前上传的请求:

326956c67f747af91e1c8f8c60cbc136.png

来对比一下正确的图片上传时 content-type 的格式:

e8a4fb691ec4fbbba5b4f6cfc238de19.png

在使用 fetch 请求时,设置 Content-type 就会丢失 boundary 参数,因此在上传文件时,不需要设置 headers 字段,浏览器会自动生成完整的 content-type(包含 boundary)。

移除 headers 字段后,fetch api可以正常的上传文件了!!

codepen 示例

Unexpected end of input 报错

其实这就是 js 语法错误,下面代码中使用 fetch api 获取资源后返回一个响应的 response 对象,如果我们指定 content-type 为 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去解析服务端返回的数据。而在服务端不按照 content-type 预定的值传回,就会得到这个报错。

// 服务端

router.post('/aa', async ctx => {

ctx.body = '

内容
'

})

复制代码

如果想简单的模拟直接使用 JSON.parse("

1

")就会得到同样的结果。下面的代码都是一个道理。

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

复制代码

所以想要正确的解析服务端的返回值,前后端要统一设定好 content-type 对应传输的数据类型,响应对象response也支持其他多个方法

整理 content-type

axios

get 请求方式

请求方式为 get 只会使用 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 Encoding,also known as percent-encoding),是因为它的编码方式非常简单,使用%百分号加上两位的字符——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

post 请求方式

application/x-www-form-urlencoded格式

默认使用该方式,使用该方式时需要对传入对参数处理成 name=hehe&age=10 格式,引入如果你传入对是一个对象,在 axios 默认配置中会将 content-type 设置为 application/json;charset=utf-8

988eabcffc69c677dc99f84857b70640.png

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)

}

复制代码

text/plain

axios.post('http://localhost:8899/react/aa','我就是内容',{

headers:{

'content-type':'text/plain'

}

})

复制代码

8c09a324040cbeae1d703c424c2c65e1.png

3. multipart/form-data

上传文件是不需要设置该类型,浏览器会自动添加!!!

4. application/json

axios.post('http://localhost:8899/react/aa',{name:'dd',age:18})

fetch

get 方式 application/x-www-form-urlencoded 编码

get 传参数的方式需要添加到路径上,所以 Url编码的工作需要我们手动实现

// 在URL中写上传递的参数

fetch('http://localhost:8080?a=1&b=2', {

method: 'GET'

})

// 处理传入的 params 参数

for(let key in params){

param += `${key}=${encodeURIComponent(params[key])}&`

}

复制代码

post 方式

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'

})

复制代码

application/json

需要使用 JSON.stringify() 将对象转换成 JSON 字符串,body作为接受数据的字段

fetch(url, {

method: 'POST', // or 'PUT'

body: JSON.stringify(data),

headers: new Headers({

'Content-Type': 'application/json'

})

})

复制代码

multipart/form-data

上传文件是不需要设置该类型,浏览器会自动添加!!!

let data = new FormData()

data.append('file',target.files[0])

fetch(url,{

body:data,

method:"POST",

headers:{}

})

复制代码

参考

String filePath=""; File file=new File(filePath); //解决乱码问题 String filename=URLEncoder.encode(file.getName(),"utf-8"); //重置输出流 response.reset(); Response.AddHeader("Content-Disposition", "attachment;filename="+filename);//设置文件名 response.addHeader("Content-Length",file.length);//设置下载文件大小 response.setContentType("application/octet-stream");//设置文件类型 OutputStream toClient=new BufferedOuntputStream( response.getOutputStream() );//获取二进制输出流 //读取文件数据 InputStream fis=new BufferedInputStream(new FileInputStream(filePath)); byte[] buffer=new byte[file.length()]; fis.read(buffer); fis.close(); //输出文件数据 toClient.write(buffer); toClient.close(); 文件类型如下: ".*"="application/octet-stream" ".001"="application/x-001" ".301"="application/x-301" ".323"="text/h323" ".906"="application/x-906" ".907"="drawing/907" ".a11"="application/x-a11" ".acp"="audio/x-mei-aac" ".ai"="application/postscript" ".aif"="audio/aiff" ".aifc"="audio/aiff" ".aiff"="audio/aiff" ".anv"="application/x-anv" ".asa"="text/asa" ".asf"="video/x-ms-asf" ".asp"="text/asp" ".asx"="video/x-ms-asf" ".au"="audio/basic" ".avi"="video/avi" ".awf"="application/vnd.adobe.workflow" ".biz"="text/xml" ".bmp"="application/x-bmp" ".bot"="application/x-bot" ".c4t"="application/x-c4t" ".c90"="application/x-c90" ".cal"="application/x-cals" ".cat"="application/vnd.ms-pki.seccat" ".cdf"="application/x-netcdf" ".cdr"="application/x-cdr" ".cel"="application/x-cel" ".cer"="application/x-x509-ca-cert" ".cg4"="application/x-g4" ".cgm"="application/x-cgm" ".cit"="application/x-cit" ".class"="java/*" ".cml"="text/xml" ".cmp"="application/x-cmp" ".cmx"="application/x-cmx" ".cot"="application/x-cot" ".crl"="application/pkix-crl" ".crt"="application/x-x509-ca-cert" ".csi"="application/x-csi" ".css"="text/css" ".cut"="application/x-cut" ".dbf"="application/x-dbf" ".dbm"="application/x-dbm" ".dbx"="application/x-dbx" ".dcd"="text/xml" ".dcx"="application/x-dcx" ".der"="application/x-x509-ca-cert" ".dgn"="application/x-dgn" ".dib"="application/x-dib" ".dll"="application/x-msdownload" ".doc"="application/msword" ".dot"="application/msword" ".drw"="application/x-drw" ".dtd"="text/xml" ".dwf"="Model/vnd.dwf" ".dwf"="application/x-dwf" ".dwg"="application/x-dwg" ".dxb"="application/x-dxb" ".dxf"="application/x-dxf" ".edn"="application/vnd.adobe.edn" ".emf"="application/x-emf" ".eml"="message/rfc822" ".ent"="text/xml" ".epi"="application/x-epi" ".eps"="application/x-ps" ".eps"="application/postscript" ".etd"="application/x-ebx" ".exe"="application/x-msdownload" ".fax"="image/fax" ".fdf"="application/vnd.fdf" ".fif"="application/fractals" ".fo"="text/xml" ".frm"="application/x-frm" ".g4"="application/x-g4" ".gbr"="application/x-gbr" ".gcd"="application/x-gcd" ".gif"="image/gif" ".gl2"="application/x-gl2" ".gp4"="application/x-gp4" ".hgl"="application/x-hgl" ".hmr"="application/x-hmr" ".hpg"="application/x-hpgl" ".hpl"="application/x-hpl" ".hqx"="application/mac-binhex40" ".hrf"="application/x-hrf" ".hta"="application/hta" ".htc"="text/x-component" ".htm"="text/html" ".html"="text/html" ".htt"="text/webviewhtml" ".htx"="text/html" ".icb"="application/x-icb" ".ico"="image/x-icon" ".ico"="application/x-ico" ".iff"="application/x-iff" ".ig4"="application/x-g4" ".igs"="application/x-igs" ".iii"="application/x-iphone" ".img"="application/x-img" ".ins"="application/x-internet-signup" ".isp"="application/x-internet-signup" ".IVF"="video/x-ivf" ".java"="java/*" ".jfif"="image/jpeg" ".jpe"="image/jpeg" ".jpe"="application/x-jpe" ".jpeg"="image/jpeg" ".jpg"="image/jpeg" ".jpg"="application/x-jpg" ".js"="application/x-javascript" ".jsp"="text/html" ".la1"="audio/x-liquid-file" ".lar"="application/x-laplayer-reg" ".latex"="application/x-latex" ".lavs"="audio/x-liquid-secure" ".lbm"="application/x-lbm" ".lmsff"="audio/x-la-lms" ".ls"="application/x-javascript" ".ltr"="application/x-ltr" ".m1v"="video/x-mpeg" ".m2v"="video/x-mpeg" ".m3u"="audio/mpegurl" ".m4e"="video/mpeg4" ".mac"="application/x-mac" ".man"="application/x-troff-man" ".math"="text/xml" ".mdb"="application/msaccess" ".mdb"="application/x-mdb" ".mfp"="application/x-shockwave-flash" ".mht"="message/rfc822" ".mhtml"="message/rfc822" ".mi"="application/x-mi" ".mid"="audio/mid" ".midi"="audio/mid" ".mil"="application/x-mil" ".mml"="text/xml" ".mnd"="audio/x-musicnet-download" ".mns"="audio/x-musicnet-stream" ".mocha"="application/x-javascript" ".movie"="video/x-sgi-movie" ".mp1"="audio/mp1" ".mp2"="audio/mp2" ".mp2v"="video/mpeg" ".mp3"="audio/mp3" ".mp4"="video/mpeg4" ".mpa"="video/x-mpg" ".mpd"="application/vnd.ms-project" ".mpe"="video/x-mpeg" ".mpeg"="video/mpg" ".mpg"="video/mpg" ".mpga"="audio/rn-mpeg" ".mpp"="application/vnd.ms-project" ".mps"="video/x-mpeg" ".mpt"="application/vnd.ms-project" ".mpv"="video/mpg" ".mpv2"="video/mpeg" ".mpw"="application/vnd.ms-project" ".mpx"="application/vnd.ms-project" ".mtx"="text/xml" ".mxp"="application/x-mmxp" ".net"="image/pnetvue" ".nrf"="application/x-nrf" ".nws"="message/rfc822" ".odc"="text/x-ms-odc" ".out"="application/x-out" ".p10"="application/pkcs10" ".p12"="application/x-pkcs12" ".p7b"="application/x-pkcs7-certificates" ".p7c"="application/pkcs7-mime" ".p7m"="application/pkcs7-mime" ".p7r"="application/x-pkcs7-certreqresp" ".p7s"="application/pkcs7-signature" ".pc5"="application/x-pc5" ".pci"="application/x-pci" ".pcl"="application/x-pcl" ".pcx"="application/x-pcx" ".pdf"="application/pdf" ".pdf"="application/pdf" ".pdx"="application/vnd.adobe.pdx" ".pfx"="application/x-pkcs12" ".pgl"="application/x-pgl" ".pic"="application/x-pic" ".pko"="application/vnd.ms-pki.pko" ".pl"="application/x-perl" ".plg"="text/html" ".pls"="audio/scpls" ".plt"="application/x-plt" ".png"="image/png" ".png"="application/x-png" ".pot"="application/vnd.ms-powerpoint" ".ppa"="application/vnd.ms-powerpoint" ".ppm"="application/x-ppm" ".pps"="application/vnd.ms-powerpoint" ".ppt"="application/vnd.ms-powerpoint" ".ppt"="application/x-ppt" ".pr"="application/x-pr" ".prf"="application/pics-rules" ".prn"="application/x-prn" ".prt"="application/x-prt" ".ps"="application/x-ps" ".ps"="application/postscript" ".ptn"="application/x-ptn" ".pwz"="application/vnd.ms-powerpoint" ".r3t"="text/vnd.rn-realtext3d" ".ra"="audio/vnd.rn-realaudio" ".ram"="audio/x-pn-realaudio" ".ras"="application/x-ras" ".rat"="application/rat-file" ".rdf"="text/xml" ".rec"="application/vnd.rn-recording" ".red"="application/x-red" ".rgb"="application/x-rgb" ".rjs"="application/vnd.rn-realsystem-rjs" ".rjt"="application/vnd.rn-realsystem-rjt" ".rlc"="application/x-rlc" ".rle"="application/x-rle" ".rm"="application/vnd.rn-realmedia" ".rmf"="application/vnd.adobe.rmf" ".rmi"="audio/mid" ".rmj"="application/vnd.rn-realsystem-rmj" ".rmm"="audio/x-pn-realaudio" ".rmp"="application/vnd.rn-rn_music_package" ".rms"="application/vnd.rn-realmedia-secure" ".rmvb"="application/vnd.rn-realmedia-vbr" ".rmx"="application/vnd.rn-realsystem-rmx" ".rnx"="application/vnd.rn-realplayer" ".rp"="image/vnd.rn-realpix" ".rpm"="audio/x-pn-realaudio-plugin" ".rsml"="application/vnd.rn-rsml" ".rt"="text/vnd.rn-realtext" ".rtf"="application/msword" ".rtf"="application/x-rtf" ".rv"="video/vnd.rn-realvideo" ".sam"="application/x-sam" ".sat"="application/x-sat" ".sdp"="application/sdp" ".sdw"="application/x-sdw" ".sit"="application/x-stuffit" ".slb"="application/x-slb" ".sld"="application/x-sld" ".slk"="drawing/x-slk" ".smi"="application/smil" ".smil"="application/smil" ".smk"="application/x-smk" ".snd"="audio/basic" ".sol"="text/plain" ".sor"="text/plain" ".spc"="application/x-pkcs7-certificates" ".spl"="application/futuresplash" ".spp"="text/xml" ".ssm"="application/streamingmedia" ".sst"="application/vnd.ms-pki.certstore" ".stl"="application/vnd.ms-pki.stl" ".stm"="text/html" ".sty"="application/x-sty" ".svg"="text/xml" ".swf"="application/x-shockwave-flash" ".tdf"="application/x-tdf" ".tg4"="application/x-tg4" ".tga"="application/x-tga" ".tif"="image/tiff" ".tif"="application/x-tif" ".tiff"="image/tiff" ".tld"="text/xml" ".top"="drawing/x-top" ".torrent"="application/x-bittorrent" ".tsd"="text/xml" ".txt"="text/plain" ".uin"="application/x-icq" ".uls"="text/iuls" ".vcf"="text/x-vcard" ".vda"="application/x-vda" ".vdx"="application/vnd.visio" ".vml"="text/xml" ".vpg"="application/x-vpeg005" ".vsd"="application/vnd.visio" ".vsd"="application/x-vsd" ".vss"="application/vnd.visio" ".vst"="application/vnd.visio" ".vst"="application/x-vst" ".vsw"="application/vnd.visio" ".vsx"="application/vnd.visio" ".vtx"="application/vnd.visio" ".vxml"="text/xml" ".wav"="audio/wav" ".wax"="audio/x-ms-wax" ".wb1"="application/x-wb1" ".wb2"="application/x-wb2" ".wb3"="application/x-wb3" ".wbmp"="image/vnd.wap.wbmp" ".wiz"="application/msword" ".wk3"="application/x-wk3" ".wk4"="application/x-wk4" ".wkq"="application/x-wkq" ".wks"="application/x-wks" ".wm"="video/x-ms-wm" ".wma"="audio/x-ms-wma" ".wmd"="application/x-ms-wmd" ".wmf"="application/x-wmf" ".wml"="text/vnd.wap.wml" ".wmv"="video/x-ms-wmv" ".wmx"="video/x-ms-wmx" ".wmz"="application/x-ms-wmz" ".wp6"="application/x-wp6" ".wpd"="application/x-wpd" ".wpg"="application/x-wpg" ".wpl"="application/vnd.ms-wpl" ".wq1"="application/x-wq1" ".wr1"="application/x-wr1" ".wri"="application/x-wri" ".wrk"="application/x-wrk" ".ws"="application/x-ws" ".ws2"="application/x-ws" ".wsc"="text/scriptlet" ".wsdl"="text/xml" ".wvx"="video/x-ms-wvx" ".xdp"="application/vnd.adobe.xdp" ".xdr"="text/xml" ".xfd"="application/vnd.adobe.xfd" ".xfdf"="application/vnd.adobe.xfdf" ".xhtml"="text/html" ".xls"="application/vnd.ms-excel" ".xls"="application/x-xls" ".xlw"="application/x-xlw" ".xml"="text/xml" ".xpl"="audio/scpls" ".xq"="text/xml" ".xql"="text/xml" ".xquery"="text/xml" ".xsd"="text/xml" ".xsl"="text/xml" ".xslt"="text/xml" ".xwd"="application/x-xwd" ".x_b"="application/x-x_b" ".x_t"="application/x-x_t"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值