发布时间:2023-11-11 分类: 行业资讯
前端不能像本机APP那样直接操作本地文件。否则,打开网页可能会盗取用户计算机上的文件,因此需要由用户触发。用户可以通过以下三种方式触发操作:
通过输入类型=”文件”
选择本地文件通过拖放拖放文件
复制并粘贴在编辑框中
第一种是最常用的方法,通常是自定义按钮,然后放在它上面,因为type=” file”输入不会改变风格。在以下代码中编写选择控件并将其放在以下格式中:
<形式>
< input type='file'id='file-input'name='fileContent'>
< /形式>
然后,您可以使用FormData获取整个表单的内容:
$('file-input')。on('change',function(){
Console.log(`文件名是$ {this.value}`);
设formData=new FormData(this.form);
formData.append('fileName',this.value);
CONSOLE.LOG(FORMDATA);
});
打印输入值和formData,如下所示:
您可以看到文件的路径是虚假路径,这意味着浏览器无法获取文件的实际存储位置。同时,FormData打印出一个空的Objet,但这并不意味着它的内容是空的,但它对前端开发人员来说是透明的。无法查看,修改和删除内容。只有追加才能添加字段。
FormData无法获取文件的内容,而FileReader可以读取整个文件的内容。用户选择文件后,input.files可以获取用户选择的文件,如下所示:
$('file-input')。on('change',function(){
让fileReader=new FileReader(),
fileType=this.files [0] .type;
fileReader.onload=function(){
如果(/^image/.test(fileType)) {
//在fileReader.result中读取结果
$(`< img src='$ {this.result}'>`)。appendTo('body');
}
}
//打印原始文件对象
CONSOLE.LOG(this.files [0]);
//base64 mode read
fileReader.readAsDataURL(this.files [0]);
});
像这样打印原始的File对象:
它是window.File的一个实例,它包含文件的修改时间,文件名,文件大小,文件的mime类型等。如果需要限制上载文件的大小,可以确定size属性是否为super。单位是字节。要确定它是否是图像文件,您可以从类型类型开始。通过判断文件名的后缀可能是不准确的,这种判断会更准确。上面的代码使用了常规判断。如果是图像,则将其分配给img的src并添加到dom。但实际上,这段代码有点问题,也就是说,并非所有的web图像都可以通过img标签显示。出来,通常是jpg/png/gif,所以你应该判断图像格式。如果你可以将判断改为:
/^图像/[JPEG | PNG | GIF] /测试(this.type)
然后实例化FileReader,调整其readAsDataURL并将其传递给File对象,监听其onload事件,并在其result属性中加载读取结果。它是一种base64格式,可以直接分配给img src。
除了可读为base64之外,还可以按以下格式读取FileReader:
//由base64读取,结果为base64,任何文件都可以转换为base64格式
fileReader.readAsDataURL(this.files [0]);
//作为二进制字符串读取,结果是二进制内容的utf-8形式,已被弃用
fileReader.readAsBinaryString(this.files [0]);
//读取原始二进制文件,结果可以直接转换为整数数组
fileReader.readAsArrayBuffer(this.files [0]);
另一个主要读作ArrayBuffer,它是原始二进制格式的结果。像这样打印出ArrayBuffer:
正如您所看到的,它对前端开发人员也是透明的。它无法直接读取内容,但可以通过ArrayBuffer.length获取长度。它也可以转换为整数数组,以了解文件的原始二进制内容:/p>
设buffer=this.result;
//依次读取每字节8位,将其放入整数数组
设view=new Uint8Array(buffer);
CONSOLE.LOG(视图);
如果您使用第二种拖动方法,您应该如何阅读文件?以下html(样式省略):
< div class='img-container'>
放下你的图像
</DIV>
这将在页面上显示一个框:
然后听听它的拖放事件:
$('。img-container')。on('dragover',function(event){
event.preventDefault();
})
.on('drop',function(event){
event.preventDefault();
//数据位于事件的dataTransfer对象
中设file=event.originalEvent.dataTransfer.files [0];
//然后你可以使用FileReader来操作
fileReader.readAsDataURL(文件);
//或添加到FormData
设formData=new FormData();
formData.append('fileContent',file);
})
数据位于drop事件的event.dataTransfer.files中。获取File对象后,可以使用输入框执行相同的操作,即使用FileReader读取或创建空的formData,然后将其附加到formData。 。
第三种粘贴方式通常在编辑框中完成,例如将div的contenteditable设置为true:
< div contenteditable='true'>
您好,请在此处粘贴图片
</DIV>
粘贴的数据位于event.clipboardData.files:
中$('editor')。on('paste',function(event){
设file=event.originalEvent.clipboardData.files [0];
});
但Safari的粘贴不会通过事件传递,而是直接在输入框中添加图片,如下所示:
它创建一个img标记并将img的src指向blob的本地数据。什么是blob,如何读取blob的内容?
Blob是类文件的存储格式,可以以几乎任何格式存储内容,例如json:
设data={hello:'world'};
设blob=new Blob([JSON.stringify(data)],
{type:'application/json'} ;;
为了获取本地blob数据,我们可以使用ajax发送本地请求:
$('editor')。on('paste',function(event){
//需要setTimeout 0和其他图像再次处理
setTimeout(()=> {
设img=$(this).find('img [src ^='blob']')[0];
CONSOLE.LOG(img.src);
//使用xhr
获取blob数据设xhr=new XMLHttpRequest();
Xhr.open('GET',img.src);
//更改mime类型
xhr.responseType='blob';
Xhr.onload=function(){
//响应是一个Blob对象
CONSOLE.LOG(this.response);
};
Xhr.send();
},0);
});
上面的代码打印出这样的blob:
它可以获得它的大小和类型,但具体内容也是不可见的。它有一个切片方法,可用于剪切大文件。与File类似,您可以使用FileReader读取其内容:
函数readBlob(blobImg){
让fileReader=new FileReader();
fileReader.onload=function(){
CONSOLE.LOG(this.result);
}
fileReader.onerror=function(err){
CONSOLE.LOG(ERR);
}
fileReader.readAsDataURL(blobImg);
}
readBlob(this.response);
另外,你也可以使用window.URL来读取,这是一个新的API,经常与Service Worker一起使用,因为SW经常要解析url。以下代码:
函数readBlob(blobImg){
设urlCreator=window.URL || window.webkitURL;
//获取base64结果
设imageUrl=urlCreator.createObjectURL(this.response);
返回imageUrl;
}
readBlob(this.response);
关于src是使用blob链接,除了上面提到的img之外,另一个很常见的是视频标签,比如youtobe视频是使用blob:
这些数据不是直接本地的,而是由视频数据不断请求,然后通过blob容器媒体添加到视频中,它也是通过URL API创建的:
让mediaSource=new MediaSource();
Video.src=URL.createObjectURL(mediaSource);
让sourceBuffer=mediaSource.addSourceBuffer('video/mp4; codecs='avc1.42E01E,mp4a.40.2'');
sourceBuffer.appendBuffer(BUF);
我没有具体实践过,也不会讨论它。
上面,我们用三种方法获取文件的内容,最后得到:
FormData格式
FileReader读取的base64或ArrayBuffer二进制格式
如果它直接是FormData,那么就直接用ajax发布就行了,不需要做任何事情:
设form=document.querySelector('form'),
formData=new FormData(form),
formData.append('fileName','photo.png');
设xhr=new XMLHttpRequest();
//假设上传文件的界面叫做upload
Xhr.open('POST','/upload');
Xhr.send(FORMDATA);
如果使用jQuery,请将两个属性设置为false:
$就({
网址:'/upload',
输入:'POST',
数据: formData,
processData: false,//不处理数据
contentType: false //不设置内容类型
});
因为jQuery会自动转义内容,并根据数据自动设置请求mime类型,告诉jQuery直接用xhr.send发送它。
观察控制台发送的数据:
< form enctype='multipart/form-data'method='post'>
< input type='file'name='fileContent'>
< /形式>
如果xhr.send是FormData类型,它将自动设置enctype。如果您使用默认表单提交上载的文件,则必须在表单上设置此属性,因为上载文件只能使用此POST编码。常用的POST编码是application/x-www-form-urlencoded,与GET一样,使用&编码器。参数和参数之间的连接,例如:
键1=值&安培;键2=值
特殊字符正在逃避。此数据POST放在请求正文中,GET拼写在网址上。如果使用jq,jq将帮助你拼写和转义。
用于上传文件的multipart/form-data通过参数和参数与相同的字符串分开。使用以上内容:
&MDASH;&MDASH; WebKitFormBoundary72yvM25iSPYZ4a3F
这个字符通常变得更长,更随机,因为它保证字符串不会出现在正常内容中,因此内容的特殊字符不需要转义。
请求的contentType由浏览器设置为:
内容类型:
多部分/格式的数据;边界=&MDASH; -WebKitFormBoundary72yvM25iSPYZ4a3F
后端服务知道如何通过此解析这样的数据。 (通常使用的框架是处理的,特定的接口不需要关心它应该如何解决)
如果读取的结果是ArrayBuffer,它也可以由xhr.send直接发送,但通常我们不会直接发送文件的内容,而是使用等于文件内容的字段名称。如果您将其作为ArrayBuffer读取然后上传它,效果不是很大。最好使用formData直接添加File对象的内容,因为上述三种方法都可以获取File对象。如果从ArrayBuffer开始,可以将其转换为blob,然后将其附加到FormData。
更常用的应该是base64,因为前端经常需要处理图像,在读取它为base64之后,它可以被绘制到画布中,然后你可以做一些处理,比如压缩,裁剪,旋转和等等。最后,使用canvas导出base64格式的图像,如何上传base64格式?
第一种是拼写由表单上传的multipart/form-data的格式,然后用xhr.sendAsBinary发送出来,如下所示:
设base64Data=base64Data.replace(/^ data: image/[^;] +; base64,/,'');
设border='---------- boundaryasoifvlkasldvavoadv';
xhr.sendAsBinary([
//name=data
边界,
'内容 - 处置:表单数据;命名=“数据”; filename=''+ fileName +''',
'Content-Type:'+'image /'+ fileType,'',
Atob(base64Data),边界,
//名称=IMAGETYPE
边界,
'内容 - 处置:表单数据; name='imageType'','',
的fileType,
边界+' - '
]。加入('“));
上面的代码使用了window.atob的api,它将base64恢复为原始内容的字符串表示形式,如下所示:
Btoa将内容转换为base64编码,而atob则恢复base64。在调优atob之前,您需要删除表示不属于base64内容的内容格式的字符串,即上述代码第一行的替换处理。
这与使用formData类似,但由于不推荐使用sendAsBinary,因此新代码不建议使用此方法。那我们该怎么办?
您可以将base64转换为blob,然后附加到formData。以下函数(从b64到blob)可以将base64转换为blob:
函数b64toBlob(b64Data,contentType,sliceSize){
contentType=contentType || “”;
sliceSize=sliceSize || 512;
Var byteCharacters=atob(b64Data);
Var byteArrays=[];
For(var offset=0; offset< byteCharacters.length; offset +=sliceSize){
Var slice=byteCharacters.slice(offset,offset + sliceSize);
Var byteNumbers=new Array(slice.length);
对于(var i=0; i< slice.length; i ++){
byteNumbers [i]=slice.charCodeAt(i);
}
Var byteArray=new Uint8Array(byteNumbers);
byteArrays.push(字节阵列);
}
Var blob=new Blob(byteArrays,{type: contentType});
返回blob;
}
然后你可以追加到formData:
设blob=b64toBlob(b64Data,'image/png'),
formData=new FormData();
formData.append('fileContent',blob);
这样您就不必自己拼写多部分/表单数据格式数据。
用于处理和上传上述文件的API可与IE10 +兼容。如果我想与旧浏览器兼容,我该怎么办?
你可以使用iframe,原理是默认表单表单提交会刷新页面,或者跳转到目标指定的url,但如果ifrmae目标指向iframe,那么刷新就是iframe,返回结果也会在ifame中显示,然后获取此ifrmae的内容以获取上传界面返回的结果。
以下代码:
设iframe=document.createElement('iframe');
Iframe.display='none';
iframe.name='form-iframe';
document.body.appendChild(IFRAME);
//更改表单的目标
Form.target='form-iframe';
iframe.onload=function(){
//获取iframe的内容,即服务返回的数据
设responseText=this.contentDocument.body.textContent
|| this.contentWindow.document.body.textContent;
};
Form.submit();
Form.submit将触发表单提交。当请求完成(成功或失败)时,将触发iframe的onload事件,然后在onload事件中检索返回的数据。如果请求失败,则iframe的内容将为空。该判决请求已成功。
没有办法使用iframe获取上传进度,使用xhr来获取当前上传的进度,这是在XMLHttpRequest 2.0中引入的:
Xhr.upload.onprogress=function(event){
If(event.lengthComputable){
//当前上传进度的百分比
duringCallback((event.loaded/event.total)* 100);
}
};
这将成为一个真正的加载进度条。
本文讨论了交互式阅读的三种方式。您可以通过输入控件获取input.files中的File对象。拖放位于drop事件的event.dataTransfer.files中,粘贴事件将粘贴到事件中。在clipboardData.files中,Safari狂热的是将src插入编辑器中的本地img标记。您可以通过发送请求加载本地blob数据,然后通过FileReader读取它,或直接附加到formData。获取的File对象可以直接添加到FormData。如果需要读取base64格式进行处理,可以将处理后的base64转换为blob数据并将其附加到formData。对于旧版浏览器,您可以使用iframe来解决提交页面或刷新页面的问题。
总之,本地文件的前端处理和上传应该几乎相同,但是应该有很多未提及的细节,读者可以按照本文所列的方向练习。如果有其他上传方法,请告诉我。