操作文件相关数据类型

116 阅读13分钟

FormData

定义

FormDataAjax2.0(XMLHttpRequest Level2) 提供的一种将form表单元素name和value组合成键值,实现表单数据的序列化,从而减少from表单元素的拼接,提高工作效率对的接口,以方便将form表单数据通过XMLHttpRequest.send() 方法发送到后端,可以使用该对象来处理form表单元素并方便的进行文件上传。

XMLHttpRequest 是一个浏览器接口,通过它,我们可以使用Javascript进行 HTTP (S) 通信。XMLHttpRequest 在现在浏览器中是一种常用的前后台交互数据的方式。2008年 2 月,XMLHttpRequest Level 2 草案提出来了,相对于上一代,它有一些新的特性,其中FormData 就是XMLHttpRequest Level 2 新增的一个对象,利用它来提交表单、模拟表单提交,当然最大的优势就是可以上传二进制文件。

构造函数

FormData()  构造函数用于创建一个新的FormData对象。

var formData = new FormData(form)

参数

  • form 可选

一个 HTML 上的<form>表单元素——当指定了,这种方式创建的FormData对象会自动将 form 中的表单值也包含进去,包括文件内容也会被编码之后包含进去。 eg:

<form id="myForm" name="myForm">
  <div>
    <label for="username">Enter name:</label>
    <input type="text" id="username" name="username">
  </div>
  <div>
    <label for="useracc">Enter account number:</label>
    <input type="text" id="useracc" name="useracc">
  </div>
  <div>
    <label for="userfile">Upload file:</label>
    <input type="file" id="userfile" name="userfile">
  </div>
<input type="submit" value="Submit!">
</form>
var myForm = document.getElementById('myForm');
formData = new FormData(myForm);

创建一个空的 FormData 对象

var formData = new FormData(); // 当前为空

你可以使用FormData.append来添加键/值对到表单里面;

formData.append('username', 'Chris');

方法

  • FormData.append() 向 FormData 中添加新的属性值,FormData 对应的属性值存在也不会覆盖原值,而是新增一个值,如果属性不存在则新增一项属性值。

通过append(key,value)来添加数据,如果指定的key不存在则会新增一条数据,如果key存在,则追加到数据末尾

FormData.set和 append() 的区别在于,如果指定的键已经存在, FormData.set 会使用新值覆盖已有的值,而 append() 会把新值添加到已有值集合的后面。

eg:

   let formData = new FormData();
    formData.append(
        'file', // key
        item.blob, //value,
        item.fileName // name
    );
    formData.append(
        'ccc', // key
        '222'
    );
    formData.append(
        'ccc', // key
        '2333'
    );
    
    console.log(formData)

image.png

这个方法有两个版本:一个有两个参数的版本和一个有三个参数的版本。

// 语法
// name: value中包含的数据对应的表单名称
// value: 表单的值。可以是USVString 或 Blob (包括子类型,如 File)。
//filename (可选): 传给服务器的文件名称 (一个 USVString), 当一个 Blob 或 File 被作为第二个参数的时候, Blob 对象的默认文件名是 "blob"。 File 对象的默认文件名是该文件的名称。
formData.append(name, value);
formData.append(name, value, filename);
 
// 示例
var formData = new FormData(); // Currently empty
 
// 你可以通过 FormData.append 往对象里加入键值对:
formData.append('username', 'Chris');
formData.append('userpic', myFileInput.files[0], 'chris.jpg');
 
// 跟常规表单数据一样,你可以使用同一个名称添加多个值 。例如 (为了与PHP命名习惯一致在名称中添加了[]):
formData.append('userpic[]', myFileInput1.files[0], 'chris1.jpg');
formData.append('userpic[]', myFileInput2.files[0], 'chris2.jpg');
 
formData.getAll("userpic[]"); // [myFileInput1.files[0], myFileInput2.files[0]]

这项技术使得多文件上传的处理更加简单,因为所得数据结构更有利于循环。

  • FormData.get() 返回在 FormData 对象中与给定键关联的第一个值。
  • FormData.getAll() 返回一个包含 FormData 对象中与给定键关联的所有值的数组。
// 语法
// name: 将要获取值的键名
// 返回值: 包含值的FormDataEntryValue (en-US)。
formData.get(name);
 
// 例子
var formData = new FormData();
 
// 使用FormData.append方法添加两个数据:
formData.append('username', 'Chris');
formData.append('username', 'Bob');
 
// 获取key为name的第一个值
formData.get('username'); // "Chris"
 
// 返回一个数组,获取key为name的所有值
formData.getAll("username"); // ["Chris",'Bob']
  • FormData.delete() 从 FormData 对象里面删除一个键值对。 删除 FormData对象所有指定的 key 的所有值。
// 语法
// name: 要删除的键(Key)的名字
formData.delete(name);
 
// 例子
var formData = new FormData(myForm);
formData.delete('username');
  • FormData.entries() 返回一个包含所有键值对的iterator对象。
// 语法
formData.entries();
 
// 示例
// Create a test FormData object
var formData = new FormData();
formData.append('key1', 'value1');
formData.append('key2', 'value2');
 
// 每调用一次next()返回一条数据,数据的顺序由添加的顺序决定
// 返回的是一个对象,当其done属性为true时,说明已经遍历完所有的数据,这个也可以作为判断的依据
// 返回的对象的value属性以数组形式存储了一对key/value,数组下标0为key,下标1为value,如果一个key值对应多个value,会变成多对key/value返回
var i = formData.entries();
i.next(); // {done:false, value:["key1", "value1"]}
i.next(); // {done:fase, value:["key2", "value2"]}
i.next(); // {done:true, value:undefined}
 
// 方式二
for(var pair of formData.entries()) {
   console.log(pair[0]+ ', '+ pair[1]);
}
key1, value1
key2, value2
  • FormData.has() 返回一个布尔值表明 FormData 对象是否包含某些键。
// 语法
formData.has(name);
 
// 示例
var formData = new FormData();
formData.has('username'); // false
formData.append('username', 'Chris');
formData.has('username'); // true
  • FormData.keys() 返回一个包含所有键的iterator对象。
// 语法
// 返回值:返回一个迭代器( iterator)
formData.keys();
 
// 示例
// 先创建一个 FormData 对象
var formData = new FormData();
formData.append('key1', 'value1');
formData.append('key2', 'value2');
 
// 输出所有的 key
for (var key of formData.keys()) {
   console.log(key);
}
 
key1
key2
  • FormData.values() 返回一个包含所有值的iterator对象。
// 语法
// 返回值:返回一个迭代器
formData.values();
 
// 示例
//创建一个FormData测试对象
var formData = new FormData();
formData.append('key1', 'value1');
formData.append('key2', 'value2');
 
//显示值
for (var value of formData.values()) {
   console.log(value);
}
value1
value2
  • FormData.set() 给 FormData 设置属性值,如果FormData 对应的属性值存在则覆盖原值,否则新增一项属性值。
// 语法
// name: 字段名称
// value:字段的值,如果是传入两个参数的方式,那么该值是一个 USVString,如果不是,将被转成一个字符串。如果是传入三个参数的方式,那么该值将是一个布尔值(Blob),文件(File),或者一个 USVString,如果不是,将被转成一个字符串。
// filename(可选): 当第二个参数传递的是一个blob对象(Blob)或者file对象(File),filename参数就代表传给服务端的文件名(一个USVString)。Blob 对象的默认文件名是 "blob"。
 
// 该方法有两种使用方式,一个是传入两个参数,一个是传入三个参数。
formData.set(name, value);
formData.set(name, value, filename);
 
// 示例
var formData = new FormData(); // Currently empty
formData.set('username', 'Chris');
formData.set('userpic', myFileInput.files[0], 'chris.jpg');

FormData主要用途

  • 将form表单元素的name和value组合成键值对,实现表单数据序列化,提高开发效率。
  • 异步上传文件。

这里说明一下文件上传

方式1

通过后端提供的接口,以FormData数据格式进行传参,将文件传给服务器,接口返回url及name供前端使用。

image.png

image.png

方式2

通过后端提供的接口,以FormData数据格式进行传参,将文件传给服务器,接口返回id及name供前端使用。

image.png

image.png

注意:FormData 对象的操作方法,全部在原型中本身没任何的属性及方法

let formData = new FormData();
formData.append(
    'file', // key
    item.blob, //value,
    item.fileName // name
);
formData.append(
    'ccc', // key
    '222'
);
formData.append(
    'ccc', // key
    '2333'
);

console.log('formData: ', formData);

image.png

应用

使用 FormData 对象发送文件

原生ajax

eg1:

<!--HTML部分-->
<form action="">
	<label for="">
		姓名: <input type="text" name="name">
	</label>
	<label for="">
		文件:<input id="file" type="file" name="file">
 	</label>
 	<label for="">
		<input type="button" value="保存">
	</label>
</form>
//原生js上传文件
//获取按钮
var btn = document.querySelector('[type=button]');
//绑定事件
btn.onclick = function () {
     // 文件元素
    var file = document.querySelector('[type=file]');
    // 通过FormData将文件转成二进制数据
    var formData = new FormData();
    // 将文件转二进制
    formData.append('upload', file.files[0]);
    
    //创建XMLHttpRequest,用这个来发送数据
    var xhr = new XMLHttpRequest;
    /*初始化HTTP请求参数(请求方式,url,是否同步)*/
    xhr.open('post', 'file.php');
	
	/*
	xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
	设置请求头的contentType
	*/
	
    // 监听上传进度
    xhr.upload.onprogress = function (ev) {
        // 事件对象
        // console.log(ev);
        var percent = (ev.loaded / ev.total) * 100 + '%';
        console.log(percent);
        progress.style.width = percent;
    }
 
    xhr.send(formData);
 
    xhr.onreadystatechange = function () {
    //status状态为200 表示请求成功,readystate为4表示对象传递完毕或者准备就绪 
            if(xhr.readyState == 4 && xhr.status == 200) {
            }
    }
}

eg2:

//通过FormData()构造函数创建空的formData对象
let formData = new FormData();
//通过append()方法新增属性
formData.append('userName', 'Iron man');
//通过get()方法对值读取属性
console.log(formdata.get("userName"));
//通过delete()方法删除属性
formData.delete('userName');
//通过set()方法对值设置属性
formdata.set("userName","钢铁侠");
console.log(formdata.get("userName"));

//通过form表单初始化FormData
//创建表单
<form id="carList">
    <p>商品名称:<input type="text" name="productName"  value="耐克"></p>
    <p>商品数量:<select name="productNumber">
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
        <option value="4">4</option>
    </select></p>
    <p>商品价格:<select name="productPrice">
        <option value="100">100</option>
        <option value="200">200</option>
        <option value="300">300</option>
        <option value="400">400</option>
    </select></p>
    <p><input type="button" id="payment" value="支付"></p>
</form>
//表单原生作为参数初始化FormData
<script>
    //获得表单按钮元素
    var paymentBtn = document.getElementById("payment");
    //为按钮添加点击事件

    //根据ID获得页面当中的form表单元素 当前元素就是上面第二条的form表单
    var form = document.querySelector("#carList");
    //将获得的表单元素作为参数,对formData进行初始化
    var formData = new FormData(form);
    var xml = new XMLHttpRequest();
    xml.open('POST', "http:172.168.0.1");
    xml.send(formData);
    xml.onload = function (){
        console.log(xml.status);
    }

</script>

使用jQuery

eg1:

<!--HTML部分-->
<form action="">
	<label for="">
		姓名: <input type="text" name="name">
	</label>
	<label for="">
		文件:<input id="file" type="file" name="file">
 	</label>
 	<label for="">
		<input type="button" value="保存">
	</label>
</form>
//绑定提交事件
function upload() {
    //获取form表单元素
    var f = document.getElementById("myForm");
    //使用表单元素来构造FromData
    var myform = new FormData(f);
    $.ajax({
        url: "/Library/test/upload",
        type: "post",
        dataType: "json",
        data: myform,
        cache: false,                      // 不缓存
        processData: false,                // jQuery不要去处理发送的数据
        contentType: false,                // jQuery不要去设置Content-Type请求头
        success: function (data) {
            console.log(data);
        }
    });
}

使用ajax

注意:headers要加请求头

  headers: { 'Content-Type': 'multipart/form-data' }

blob

在一般的Web开发中,很少会用到Blob,但Blob可以满足一些场景下的特殊需求。BlobBinary Large Object),代表二进制类型的大对象

Blob的概念在一些数据库中有使用到,例如,MYSQL中的BLOB类型就表示二进制数据的容器。在Web中,Blob类型的对象表示不可变的类似文件对象的原始数据,通俗点说,就是Blob对象是二进制数据,但它是类似文件对象的二进制数据,因此可以像操作File对象一样操作Blob对象,实际上,File继承自Blob。

定义

Blob 对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作。

Blob 表示的不一定是 JavaScript 原生格式的数据。File 接口基于 Blob,继承了 blob 的功能并将其扩展以支持用户系统上的文件。

构造函数

Blob()  构造函数返回一个新的 Blob 对象。blob 的内容由参数数组中给出的值的串联组成。

let aBlob = new Blob(array, options );

参数:

  • array 是一个由ArrayBufferArrayBufferViewBlobDOMString 等对象构成的 Array ,或者其他类似对象的混合体,它将会被放进 Blob。DOMStrings 会被编码为 UTF-8。

  • options 是一个可选的BlobPropertyBag字典,它可能会指定如下两个属性:

    • type,默认值为 "",它代表了将会被放入到 blob 中的数组内容的 MIME 类型。
    • endings,默认值为"transparent",用于指定包含行结束符\n的字符串如何被写入。它是以下两个值中的一个:"native",代表行结束符会被更改为适合宿主操作系统文件系统的换行符,或者 "transparent",代表会保持 blob 中保存的结束符不变 (非标准)

eg1:

image.png

eg2:

image.png

实例属性

  • size
  • type

详情见MDN

Blob 对象的 type 属性给出文件的 MIME 类型。如果类型无法确定,则返回空字符串。

file

定义

文件(File)接口提供有关文件的信息,并允许网页中的 JavaScript 访问其内容。

通常情况下, File 对象是来自用户在一个 <input> 元素上选择文件后返回的 FileList 对象,也可以是来自由拖放操作生成的 DataTransfer 对象,或者来自 HTMLCanvasElement 上的 mozGetAsFile() API。在 Gecko 中,特权代码可以创建代表任何本地文件的 File 对象,而无需用户交互(有关详细信息,请参阅注意事项

File 对象是特殊类型的 Blob,且可以用在任意的 Blob 类型的 context 中。比如说, FileReaderURL.createObjectURL()createImageBitmap() (en-US), 及 XMLHttpRequest.send() 都能处理 Blob 和 File

image.png

构造函数

File()  构造器创建新的 File 对象实例

var myFile = new File(bits, name[, options]);

参数:

  • bits 一个包含ArrayBufferArrayBufferViewBlob,或者 DOMString 对象的 Array — 或者任何这些对象的组合。这是 UTF-8 编码的文件内容。

  • name USVString,表示文件名称,或者文件路径。

  • options 可选。选项对象,包含文件的可选属性。可用的选项如下:

    • typeDOMString,表示将要放到文件中的内容的 MIME 类型。默认值为 "" 。
    • lastModified: 数值,表示文件最后修改时间的 Unix 时间戳(毫秒)。默认值为 Date.now()

eg:

var file = new File(["foo"], "foo.txt", {
  type: "text/plain",
});

实例属性

Base64

定义

base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,它的目的是用ASCII中定义的可见字符去表示任意的二进制数据。之所以要这样做,是因为计算机中很多数据是只能通过可见字符去传输的(比如我们的网站网址,比如一些面向字符的网络协议如SMTP等),但是这些情景有时又需要去传输二进制数据。基于这样的需要,诞生了Base64.
简单来讲,Base64是一种用64个characters(a-z,A-Z,0-9,+,/)来表示任意二进制数据的方法。

Base64是基于64个可打印字符来表示二进制数据的编解码方式。
正因为可编解码,所以它主要的作用不在于安全性,而在于让内容能在各个网关间无错的传输。

这64个可打印字符包括大写字母A-Z、小写字母a-z、数字0-9共62个字符,再加上另外2个 + 和 /
Base64是一种索引编码,每个字符都对应一个索引,具体的关系图,如下:

image.png

相互转换方法

(1)file/Blob 转base64方法

/**
 * 获取文件的Base64
 * @param file      {File} / {Blob}     文件
 * @param callback  {Function}  回调函数,参数为获取到的base64
 */
function fileToBase64(file, callback) {
  const fileReader = new FileReader()
  fileReader.readAsDataURL(file)
  fileReader.onload = function (e) {
    // 方式1
    //callback(this.result)
    
    // 方式2
    callback(e.target.result);
  }
}

(2)base64转Blob方法

/**
 * Base64转Blob
 * @param dataURL   {String}  base64
 * @param mimeType	{String}  [可选]文件类型,默认为base64中的类型
 * @returns {File}
 */
function base64ToBlob(dataURL, mimeType = null) {
  const arr = dataURL.split(','),
  	defaultMimeType = arr[0].match(/:(.*?);/)[1],
  	bStr = atob(arr[1]),
  	n = bStr.length,
  	u8arr = new Uint8Array(n)
  let n = bStr.length
  while (n--) {
    u8arr[n] = bStr.charCodeAt(n)
  }
  return new Blob([u8arr], {type: mimeType || defaultMimeType})
}

(3)Blob转file方法

/**
 * Blob转File
 * @param blob     {Blob}   blob
 * @param fileName {String} 文件名
 * @param mimeType {String} 文件类型
 * @return {File}
 */
function blobToFile (blob, fileName, mimeType) {
  return new File([blob], fileName, {type: mimeType})
}

(4)base64转file方法

/**
 * Base64转File
 * @param dataURL   {String}  base64
 * @param fileName	{String}  文件名
 * @param mimeType	{String}  [可选]文件类型,默认为base64中的类型
 * @returns {File}
 */
function base64ToFile(dataURL, fileName, mimeType = null) {
  const arr = dataURL.split(','),
  	defaultMimeType = arr[0].match(/:(.*?);/)[1],
  	bStr = atob(arr[1]),
  	n = bStr.length,
  	u8arr = new Uint8Array(n)
  let n = bStr.length
  while (n--) {
    u8arr[n] = bStr.charCodeAt(n)
  }
  return new File([u8arr], fileName, {type: mimeType || defaultMimeType})
}

应用

(1)图片压缩和裁剪方法

/**
 * 图片压缩和尺寸裁剪
 * @param file          {File}      图片文件
 * @param quality       {Number}    生成图片质量,0.0~1.0之间,质量越小、文件越小、图片越模糊
 * @param callback      {Function}  回调方法,参数为原文件(小于阈值的情况下)或压缩后的新文件
 * @param sizeThreshold {Number}    [可选]大小阈值,单位:B,默认500K
 * @param targetWidth   {Number}    [可选]生成图片的宽度,单位:px,默认800
 * @param targetHeight  {Number}    [可选]生成图片的高度,单位:px,默认值按宽度自适应获取
 */
function pressImg (file, quality, callback, sizeThreshold = 512000, targetWidth = 800, targetHeight = null) {
  if (!file || !callback) {
    console.error('pressImg参数不完整!')
    return
  }
  if (!file.type.includes('image')) {
    console.error('文件格式非图片')
    return
  }

  fileToBase64(file, function (base64) {
    if (base64) {
      const image = new Image()
      image.src = base64
      image.onload = function () {
        if (file.size <= sizeThreshold && this.width <= targetWidth) {// 大小、宽度均小于阈值,则不需处理,返回原文件
          return callback(file)
        }
        const canvas = document.createElement('canvas')
        const context = canvas.getContext('2d')
        const scale = this.width / this.height
        canvas.width = targetWidth
        canvas.height = targetHeight || (targetWidth / scale)
        context.drawImage(image, 0, 0, canvas.width, canvas.height)
        const dataURL = canvas.toDataURL(file.type, quality)
        const newFile = base64ToFile(dataURL, file.name)
        callback(newFile)
      }
    }
  })
}

(2)利用blob对象实现大文件分片上传

常见MINE列表