有没有你不知道的 File

·  阅读 108
有没有你不知道的 File

获取 File 对象的几种途径

1、input[type=file]

通过表单元素 file input 拿取 File 对象。

MDN 参考:

<input type="file" id="file" />
复制代码
const input = document.querySelector('#file')
const files = Array.from(input.files)
files.forEach((file) => {
    // 执行 File 处理程序
})
复制代码

2、drop event

监听鼠标拖拽,获取 File 对象

MDN 参考:

<div id="file"></div>
复制代码
const file = document.querySelector('#text')
file.addEventListener('drop', function (e) {
    e.preventDefault()
    // 千万不要加 e.stopPropagation()
    if (e.dataTransfer && e.dataTransfer.files) {
        const files = Array.from(e.dataTransfer.files)
        files.forEach((file) => {
            // 执行 File 处理程序
        })
    }
})
复制代码

3、paste event

监听粘贴事件,从粘贴板中获取 File 对象

MDN 参考:

document.addEventListener('paste', function (e) {
    const files = Array.from(e.clipboardData.files)
    files.forEach(file => {
    	// 执行 File 处理程序
    })
})
复制代码

4、xhr(Blob To File)

从服务器上加载为 Blob 资源,并将 Blob 转换为 File 对象。

原理:File 是特殊类型的 Blob

MDN 参考:

封装 xhr 功能函数

/**
 * 加载远程资源为 File
 * @param {String} url 服务器资源地址
 * @param {String} name 文件名
 * @return {Promise<File>}
 */
function loadForFile(url, name) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        xhr.open('GET', url, true)
        xhr.responseType = 'blob'
        xhr.addEventListener('load', function() {
            const file = new File([xhr.response], name, {
            	type: xhr.response.type,
                lastModified: new Date(),
            })
            resolve(file)
        })
        xhr.addEventListener('error', reject)
        xhr.send()
    })
}
复制代码

xhr 功能函数的使用

loadForFile('http://xxx/tt.mp3', 'tt.mp3').then((file) => {
    // 执行 File 处理程序
})
复制代码

canvas(Blob To File)

canvas 上获取 File 有两种方式,一种是已经被弃用的 HTMLCanvasElement.mozGetAsFile(知道即可);另一种则是同 xhr 一样的原理,通过 BlobFile

MDN 参考:

简单的示例,更多示例请移步图片压缩

canvas.toBlob(function call(blob) {
    const file = new File([blob], 'image.png', { type: blob.type })
    // 执行 File 处理程序
}, 'image/png')
复制代码

File 资源预览

MDN 参考:

// 创建 URL 对象
const url = URL.createObjectURL(file)

// 预览图片
document.body.insertAdjacentHTML('beforeend', `<img src="${url}" />`)

// 预览音频
document.body.insertAdjacentHTML('beforeend', `<audio controls src="${url}"></audio>`)

// 预览视频
document.body.insertAdjacentHTML('beforeend', `<video controls src="${url}"></video>`)

// 预览 PDF
document.body.insertAdjacentHTML('beforeend', `<iframe src="${url}"></iframe>`)

// 释放 URL 对象(建议异步释放资源,否则 img/audio/video/iframe 标签可能会资源加载失败,影响预览效果)
setTimeout(() => {
	URL.revokeObjectURL(url)
}, 5000)
复制代码

读取 File 资源

MDN 参考:

封装资源读取功能函数

/**
 * @param {File|Blob} file 二进制对象(File类型 或 Blob类型)
 * @param {String} type 以怎样的方式读取
 * @param {String} encoding 传入一个字符串类型的编码类型,如缺省,则默认为“utf-8”类型
 * @return {Promise<String | ArrayBuffer>}
 */
function read(file, type, encoding = 'utf-8') {
    return new Promise((resolve, reject) => {
        const reader = new FileReader()
        // 返回错误信息
        reader.addEventListener('error', function () {
            reject(new Error('FileReader 读取资源失败'))
        })
        // 读取成功,返回读取到的资源
        reader.addEventListener('load', function () {
          resolve(reader.result)
        })
        // 不同的读取方式
        switch(type) {
            case 'text':
                reader.readAsText(file, encoding)
                break
            case 'base64':
                reader.readAsDataURL(file)
                break
            // case 'buffer':
            default:
                reader.readAsArrayBuffer(file)
                break
        }
    })
}
复制代码

案例:获取文本内容(FileReader.readAsText)

读取文本内容示例:HTML 部分

<input type="file" accept="text/plain" id="file"  />
<textarea rows="3" cols="50" id="tarea"></textarea>
复制代码

读取文本内容示例:JavaScript 部分

const input = document.querySelector('#file')
const tarea = document.querySelector('#tarea')

input.addEventListener('change', function() {
    const file = input.files[0]
    if (file) {
    	read(file, 'text').then(text => {
            tarea.textContent = text
        }).catch(({ message }) => {
            alert(message)
        })
    }
})
复制代码

案例:图片压缩(FileReader.readAsDataURL)

MDN 参考:

封装图片压缩功能函数

/**
 * 图片压缩
 * @param {File|Blob} file 二进制对象(File类型 或 Blob类型)
 * @param {Number} ratio 图片压缩的比例,介于 0 ~ 1 之间的数字
 * @param {Number} encoderOptions 当图片格式为 image/jpeg 或者 image/webp 时用来指定图片展示质量,介于 0 ~ 1 之间的数字
 * @return {Promise<{ file: File, base64: String }>}
 */
function compress(file, ratio = 0.75, encoderOptions = 0.92) {
    return read(file, 'base64').then(base64 => {
        return new Promise((resolve, reject) => {
            const image = new Image()
            image.addEventListener('error', function () {
                reject(new Error('compress 图片资源加载失败'))
            })
            image.addEventListener('load', function () {
                // 验证压缩比例是否合法(0~1 之间的数)
                ratio = ratio > 1 ? 1 : ratio < 0 ? 0 : ratio
                
                // 计算压缩后图片的宽高
                const width = image.naturalWidth * ratio
                const height = image.naturalHeight * ratio
                
                // 初始化 canvas
                const canvas = document.createElement('canvas')
                canvas.width = width
                canvas.height = height
                
                // 绘制图片
                const ctx = canvas.getContext('2d')
                ctx.clearRect(0, 0, width, height)
                ctx.drawImage(image, 0, 0, width, height)
                
                // canvas.toBlob 的回到函数
                function call(blob) {
                    resolve({
                        file: new File([blob], file.name, { type: file.type }), // 将 file.type 作为自定义参数可以实现图片的格式转换
                        base64: canvas.toDataURL(file.type, encoderOptions),    // 返回 base64 的图片资源便于效果预览
                    })
                }
                
                canvas.toBlob(call, file.type, encoderOptions)
            })
            image.src = base64
        })
    })
}
复制代码

图片压缩案例:HTML 部分

<input type="file" accept="image/*" id="file" />
<img src="" id="view1" alt="原图预览">
<img src="" id="view2" alt="压缩图预览">
复制代码

图片压缩案例:JavaScript 部分

const input = document.querySelector('#file')
const img1 = document.querySelector('#view1')
const img2 = document.querySelector('#view2')

input.addEventListener('change', function () {
    if (input.files[0]) {
        const file = input.files[0]
        // 原图预览
        img1.src = URL.createObjectURL(file)
        // 对图片的压缩比为 0.5
        compress(file, 0.5).then((data) => {
            // 压缩图预览
            img2.src = data.base64
            console.log(data)
        }).catch(({ message }) => {
            console.log(message)
        })
    }
})

复制代码

案例:音频截取(FileReader.readAsArrayBuffer)

MDN 参考:

依赖库:

封装音频截取功能函数

/**
 * 音频截取
 * @param {File|Blob} file 二进制对象(File类型 或 Blob类型)
 * @param {Number} start 开始截取的时间(秒)
 * @param {Number} length 截取的时间长度(秒)
 * @return {Promise<AudioBuffer>}
 */
function cutout(file, start, length) {
    return read(file, 'buffer').then((buffer) => {
        return new Promise((resolve, reject) => {
            const ctx = new AudioContext()
            
            // 当 ArrayBuffer 转 AudioBuffer 成功解码后会被调用的回调函数
            function success(audioBuffer) {
                const channels = audioBuffer.numberOfChannels
                const rate = audioBuffer.sampleRate
                start *= rate
                // 帧数
                const frame = rate * length
                // 创建采用率、同样声道数量,长度均相等的空 AudioBuffer
                const temp = ctx.createBuffer(channels, frame, rate)
                // 创建临时的Array存放复制的buffer数据
                const f32a = new Float32Array(frame)
                // 声道的数据的复制和写入
                for (let channel = 0; channel < channels; channel++) {
                    audioBuffer.copyFromChannel(f32a, channel, start)
                    temp.copyToChannel(f32a, channel, 0)
                }
                resolve(temp)
            }
            
            // 当 ArrayBuffer 转 AudioBuffer 解码失败后的的回调函数
            function error() {
                reject(new Error('ArrayBuffer 转 AudioBuffer 失败'))
            }
            
            // ArrayBuffer 转 AudioBuffer
            ctx.decodeAudioData(buffer, success, error)
        })
    })
}

/**
 * 将 AudioBuffer 转为 Blob
 * @param {AudioBuffer} buffer 音频资源
 * @return {Blob}
 */
function bufferToBlob(buffer) {
    // toWavm 为外部依赖库:https://www.npmjs.com/package/audiobuffer-to-wav
    return new Blob([new DataView(toWavm(buffer))], {
        type: 'audio/wav'
    })
}
复制代码

音频截取示例:HTML 部分

<input type="file" name="file" id="file" />
<audio src="" controls id="view1"></audio>
<audio src="" controls id="view2"></audio>
复制代码

音频截取示例:JavaScript 部分

const input = document.querySelector('#file')
const audio1 = document.querySelector('#view1')
const audio2 = document.querySelector('#view2')

input.addEventListener('change', function () {
    if (input.files[0]) {
        const file = input.files[0]
        // 原音频预览
        audio1.src = URL.createObjectURL(file)
        // 从第 10 秒开始截取 15 秒长度的音频
        cutout(file, 10, 15).then((buffer) => {
            const blob = bufferToBlob(buffer)
            // 截取的音频预览
            audio2.src = URL.createObjectURL(blob)
            console.log({ buffer, blob })
        })
    }
})
复制代码
分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改