Javascript:图片预览,压缩及上传

一、图片预览

图片预览有两种方式:

  1. FileReader把图片转化为base64格式的数据嵌入到HTML中。
  2. URL.createObjectURL()构造图片的URL并赋值给标签。

1.

FileReader允许浏览器异步读取用户电脑上的文件,需要用File或者Blob指定要读取的文件。

FileReader有一个方法叫.readAsDataURL(),这个方法用来读取指定Blob或者File的内容,读取完成之后readyState会变成DONE,并且触发loadend事件。

读取完成之后,result属性包含了一个url,这个url是一个Data URLData URL以base64编码的形式存储文件的数据。

已知图片的src属性即可以接受图片的url,也可以接受一个Data URL,直接把编码后的图片信息内嵌到HTML中。

那么我们可以把这个Data URL赋值给img.src,这样就可以在页面上显示图片了。

代码如下:


/* index.css */
/* 隐藏input框,因为太丑了,令人疲惫,这样我们就可以定义自己美美的输入框了 */
input {
  display: none;
}
// index.js
var log = console.log.bind(console)

$('#button').on('click', function () { // 点击按钮触发input框的click事件
    $('#chooseFile').click()
})
$('#chooseFile').on('change', function () {
    var file = this.files[0]
    // log(file)
    var reader = new FileReader()
    reader.onload = function (e) {
        // log(e.target)
        $('#preview').attr('src', e.target.result) // 把图片的base64编码形式赋值给img.src
    }
    reader.readAsDataURL(file)
})

关于Data URLs

Data URLs由4个部分组成:

  1. 前缀:data:
  2. mediatype;:一个MINE类型的声明,表示这串数据的编码类型,比如:image/png.如果缺省,默认为text/plain;charset=US-ASCII
  3. 可选的base64,:如果该数据是非文本类型的,那么要加上这个部分
  4. 数据本身

举个例子,这是一张图片的Data URL:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA1IAAASuCAYAAADI0RjrAAAMJGlDQ1BJQ0MgUHJvZmlsZQAASImVVwdUk8kWnr8kISGhBSIgJfQ......

Data URLs的好处是:

  1. 我们可以把体积比较小的图片嵌入到HTML文件中,减少HTTP请求次数。
  2. 如果图片是在服务端用程序动态生成的,每个用户显示的都不同时,直接把图片的Data URL嵌入到网页中会很方便。
  3. 如果访问外部资源很麻烦或者受限时。

Data URLs的缺点是:

  1. base64的体积会比原数据体积大约 1/3
  2. Data URL形式的图片不会被浏览器缓存,这意味着每次访问页面都要加载一次图片。这种情况可以通过CSS文件把Data URL设成背景图片的url来避免。
    比如:
div { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA1IAAASuCAYAAADI0RjrAAAMJGlDQ1BJQ0MgUHJvZmlsZQAASImVVwdUk8kWnr8kISGhBSIgJfQ......")

关于base64:

Base64编码原理与应用
阮一峰的网络日志——Base64笔记

2.

URL接口是一个包含若干静态方法的对象。
URL用于解析,构建,规范化和编码URLs。
URL接口可以在Web Worker中使用。

当使用一个没有实现该构造器的用户代理时,可以通过 Window.URL属性来访问该对象(基于 Webkit 和 Blink 内核的浏览器均可用 Window.webkitURL 代替)。

URL = URL || window.URL || window.webkitURL

URL有一个.createObjectURL()方法,该方法接收一个本地的blob作为参数,创建一个指向该blob的url,在几乎任何可以使用url的地方都可以使用.createObjectURL()方法创建的url。

这意味着,我们可以使用它来实现图片的预览:

// index.js
$('#chooseFile').on('change', function () {
    var file = this.files[0]
    var url = URL.createObjectURL(file) // 创建一个指向选择的图片的url
    // log(url)
    $('#preview').attr('src', url) // 将url赋值给img.src
})

.createObjectURL()创建的路径的格式是这样的:blob:null/元素本身组成的路径
比如:blob:null/8629b731-05a1-4b74-8a5a-b87878eba849
null表示域名为空。

URL.createObjectURL()的性能要比使用FileReader高很多,如果只是单纯想要预览图片,那么使用URL.createObjectURL()比较好。

二、图片压缩

canvas 有一个 toDataURL(type, quantity)方法,就是把canvas的数据输出为一个Data URL,该方法可以设置图片质量。利用这一特性,就可以用来压缩图片。

function compress(img) { // img为图片的Data URL
    var canvas = document.createElement('canvas') // 创建一个canvas
    var context = canvas.getContext('2d') 
    var width = img.width
    var height = img.height
    canvas.width = width // 把canvas的宽高设置为图片的宽高
    canvas.height = height
    // 开始画图
    context.fillStyle = '#fff'
    context.fillRect(0, 0, canvas.width, canvas.height)
    context.drawImage(img, 0, 0, canvas.width, canvas.height)
    var base64Data = canvas.toDataURL('image/jpeg', 0.4) // 压缩图片,质量参数太多反而会加大图片体积!
    canvas = context = null 
    // log(base64Data.length)
    img = null
    return base64Data // 返回压缩后的base64字符串流
}

需要注意的是:

  1. canvas.toDataURL的质量参数只对image/jpegimage/webp有效。
  2. 如果图像本身是image/png,则 type 参数不能为非image/png的其他类型。
  3. 质量参数默认是0.92,质量参数太大反而会增加图片的体积。

三、 图片上传

假设我们没有压缩图片,那么直接使用FormData()上传图片就可以了。
现在假设我们已经压缩了图片,那么要怎么传图片呢?

可以直接把base64的数据传到后端再解码成图片。也可以在前端先解码再传到后端,因为base64数据的体积要比原数据的体积大 1/3 。

思路如下:

  1. .atob()方法对用base-64编码过的字符串进行解码,我们现在输出会得到一串二进制字符串。
  2. .charCodeAt()方法得到每一个字符的Unicode编码。
  3. new Uint8Array();把我们得到的Unicode编码转化为类型数组,类型数组拥有二进制支持,可以用来处理二进制文件。普通数组是无法生成二进制文件的。
  4. Blob()构造函数构造一个文件。

代码如下:

// 注意b64Data必须把前面的类似于“data:image/png;base64,"的字串截掉
// contentType指图片类型,比如:image/png, image/jpeg 等等
function b64ToBlob(b64Data, contentType) {
    contentType = contentType || ''

    var byteCharacters = atob(b64Data) // 解码base64数据为二进制字符串
    var buffer = [] // 注意,Blob第一个参数必须是一个数组

    // 类型数组用来处理二进制文件
    var aBuffer = new ArrayBuffer(byteCharacters.length)
    var uBuffer = new Uint8Array(aBuffer)
    for (var i = 0; i < byteCharacters.length; i++) {
        uBuffer[i] = byteCharacters.charCodeAt(i) // 得到Unicode编码,存进类型数组
    }
    buffer.push(uBuffer)

    var blob = new Blob(buffer, { // 生成一个二进制文件
        type: contentType
    })
    // log(blob)
    return blob
}

现在已经完成了图片的预览,压缩,以及压缩后的转码。
之后就是ajax上传的问题了,先构建一个FormData,把图片append进去,传到后台。最后的完整实现:




    
// index.js

var log = console.log.bind(console)

// 上传图片
$('#chooseFile').on('change', function () {
    var file = this.files[0]
    if (!file.type.match('image.*')) { // 不是图片则返回
        alert('只能上传图片');
        return false;
    }
    var reader = new FileReader() 
    reader.onload = function () {
        var img = new Image()
        var originData = this.result

        img.onload = function () {
            var compressUrl = compress(img) // 压缩图片
            var blob = b64ToBlob(compressUrl.split(',')[1], 'image/jpeg') // 压缩成jpeg格式
            var form_data = new FormData()
            form_data.append('file', blob)
            $.ajax({
                type: 'POST',
                url: '你的url',
                data: form_data,
                processData: false, // 必须
                contentType: false, // 必须
                success: function (data, status, xhr) {
                    console.log(data)
                },
                error: function (error) {
                    console.log(error)
                }
            })
        }
        img.src = originData // 把图片数据赋值给img

    }
    reader.readAsDataURL(file) // 读取图片信息
})

// 压缩图片
function compress(img) {
    var canvas = document.createElement('canvas')
    var context = canvas.getContext('2d')
    var width = img.width
    var height = img.height
    // log(img.width, img.height)
    // log(width, height)
    canvas.width = width
    canvas.height = height
    context.fillStyle = '#fff'
    context.fillRect(0, 0, canvas.width, canvas.height)
    context.drawImage(img, 0, 0, canvas.width, canvas.height)
    var base64Data = canvas.toDataURL('image/jpeg', 0.4)
    canvas = context = null
    // log(base64Data.length)
    img = null
    return base64Data
}

// base64转化为二进制文件
function b64ToBlob(b64Data, contentType) {
    contentType = contentType || ''

    var byteCharacters = atob(b64Data) // 解码base64数据为二进制字符串
    var buffer = [] // 注意,Blob第一个参数必须是一个数组

    // 类型数组用来处理二进制文件
    var aBuffer = new ArrayBuffer(byteCharacters.length)
    var uBuffer = new Uint8Array(aBuffer)
    for (var i = 0; i < byteCharacters.length; i++) {
        uBuffer[i] = byteCharacters.charCodeAt(i) // 得到Unicode编码,存进类型数组
    }
    buffer.push(uBuffer)
    //  普通数组是无法生成二进制文件的
    var blob = new Blob(buffer, { // 生成一个二进制文件
        type: contentType
    })
    // log(blob)
    return blob
}

你可能感兴趣的:(Javascript:图片预览,压缩及上传)