HTML 5は携帯電話のアルバムを呼び出して画像を選択して圧縮して、input[file]のデフォルトのスタイルを修正します

21879 ワード

最近ではHTML 5プロジェクトで画像をアップロードする機能が使われていますが、今ではHTML 5のフロントエンドのページでも携帯電話のアルバムやカメラにアクセスできますが、携帯電話で撮った画像はすぐに何Mもアップロードされます.このように何枚かの画像をアップロードすると、ユーザーは街を罵るので、クライアントで画像を圧縮してからアップロードすることができ、ユーザーのトラフィックを節約することができます.では、フロントエンドはどのように画像を圧縮しますか?画像圧縮を実現するにはcanvasが必要です.さあ、やりましょう.
次のコードはReactで記述されています.
Input fileを使用してカメラのアルバムを呼び出す
<input type="file" multiple accept="image/*"/>

上記のinput fileをクリックすると、ポップアップウィンドウを呼び出し、カメラやアルバムから画像を取得できます.デフォルトのinput fileスタイルはブスすぎて、違和感があるので、まずスタイルを美化しましょう.ここでは2つの方法をお勧めします.
方法1:
"uploadFile" type="file" multiple accept="image/*"/>
<div className="btn-upload">
    <label htmlFor="uploadFile">    label>
div>

この方法はinput[file]のopacityを0、labelのfor属性をinput[file]のIDに設定することで、labelをクリックするとinput fileをクリックするのと同じ効果ですが、input fileは全く見えません.
方法2:
"leftFile" type="file" multiple accept="image/*" style={{display: 'none'}}/>
<a className="btn" onClick={()=>{
    this.refs.leftFile.click()
}}>Browsea>

この方法はinput displayをnoneに設定し,他の要素をクリックしてinputのclickイベントをトリガーする.
canvasを使用して画像を圧縮する
1、選択した画像を取得する
render() {
        const {
            prefixCls, className, children, style
        } = this.props

        const cls = classNames({
            [prefixCls]: true,
            [className]: className
        })

        return (
            <div
                className={cls}
                style={style}
                onClick={() => {
                    this.refs.inputFile.click()
                }}>
                <input ref="inputFile" type="file" multiple accept="image/*" onChange={this.onChange}/>
                { children } {/*           */}
            div>
        )
    }

ファイルドメインがどのように開かれているかにかかわらず、changeイベントで選択したファイルや撮影した写真を取得できます.
onChange = async (e) => {
    const { onChange, compress } = this.props
    let files = e.target.files
    const len = files.length
    let newFiles = []

    for(let i = 0; i < len; i++) {
        const result = await this.fileReader(files[i])
        const data = await this.compress(result)
        newFiles.push(data)
    }
}

2、FileReaderで画像をbase 64画像符号化に変換する
onChangeで画像を取得した後、FileReaderオブジェクトを作成する必要があります.readAsDataURLを呼び出して、data:image/jpegなどのファイルをbase 64画像符号化に変換する必要があります.base 64......このフォーマット.ファイルの読み取りが完了するとonloadメソッドがトリガーされます.onloadメソッドではe.targetを使用できます.resultはbase 64の符号化を取得するために使用され、読み出しに失敗した場合、この値はnullである.onloadは非同期の方法で、Promiseとasync,awaitを通じて非同期を同期化することができ、あまりcallbackをネストすることはありません.
//         base64
fileReader = (file) => {
    return new Promise(function (resolve, reject) {
        const reader = new FileReader();

        reader.onload = (e) => {
            resolve(e.target.result)
        };

        reader.onerror = reject

        reader.readAsDataURL(file);
    })
}

3、Imageオブジェクトを作成し、srcにfileReaderが読み取ったbase 64の結果を付与し、Imageのonloadメソッドで同様にピクチャ圧縮を処理する.
compress = (res) => {
    return new Promise(function (resolve, reject) {
        const img = new Image()
        
        img.onload = function() {    
            //    
            resolve(blob) //       
        }
        
        img.onerror = function() {
            reject(new Error('      '))
        }
        
        img.src = res;
    })
}

4、canvasで画像を圧縮する
JSを使って画像の圧縮効果を実現するのは、原理が簡単で、コアAPIはcanvasのdrawImage()を使う方法です.canvasのdrawImage()メソッドAPIは次のとおりです.
context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);

9つのパラメータがあるように見えますが、慌てずに3つのパラメータを見ることができます.
imgはピクチャオブジェクトであり,ページ上で取得したDOMオブジェクトであってもよいし,仮想DOM中のピクチャオブジェクトであってもよい.sx,sy,swidth,sheightはcanvasキャンバスに画像を配置するために領域を描き、sx,syは左上隅座標、swidth、sheightは領域サイズを指す.後の4つのパラメータが指定されていない場合、画像はこの領域内に引き伸ばされたり拡大されたりします.x,y,width,heightはcanvasキャンバスに画像が表示されるサイズと位置である.ここでwidth,heightの値がピクチャの元のサイズである場合,最終的な表現効果はピクチャがswidth,sheight領域内にクリップされることである.本稿の画像圧縮では,最後の4つのパラメータは使えず,前の5つのパラメータだけでよい.コアコード
let canvas = document.createElement('canvas');
let context = canvas.getContext('2d');
//     
context.clearRect(0, 0, targetWidth, targetHeight);
//     
context.drawImage(img, 0, 0, targetWidth, targetHeight);
//   base64    
const dataUrl = canvas.toDataURL(imageType);

canvas.toDataURL()メソッド
canvas.toDataURL(mimeType, qualityArgument)

画像をbase 64フォーマット情報、純文字の画像表現に変換できます.ここで、mimeTypeはcanvasが導出したbase 64ピクチャのタイプを表し、デフォルトはpngフォーマット、すなわちデフォルト値は「image/png」であり、jpgフォーマット「image/jpeg」やwebpなどのフォーマットに指定することもできる.fileオブジェクトのfile.typeはファイルのmimeTypeタイプで、変換時にちょうどそのまま使えます(fileオブジェクトがあれば).qualityArgumentはエクスポートしたピクチャ品質を表し、jpgとwebp形式にエクスポートした場合にのみこのパラメータが効果的で、デフォルト値は0.92で、比較的合理的なピクチャ品質出力パラメータですが、通常は設定する必要はありません.
canvas.toBlob()メソッド
canvas.toBlob(callback, mimeType, qualityArgument)

canvasをBlobファイルに変換することができ、通常はファイルアップロードに使用されます.バイナリなので、バックエンドにもっと友好的です.
toBlobメソッドは現在iOSではサポートされていません.
toDataURL()メソッドと比較して、toBlob()メソッドは非同期であるため、callbackパラメータが複数存在し、このcallbackコールバックメソッドのデフォルトの最初のパラメータは変換されたblobファイル情報であり、本稿demoのファイルアップロードはcanvasピクチャをバイナリに変換するblobファイルである.
完全なコードcom/zhangyi5628…
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
import './index.less'

export default class InputImage extends Component {
    constructor(props) {
        super(props)
    }

    static defaultProps = {
        prefixCls: 'yh-input-image',
        compress: true,
        className: '',
        style: null,
        onChange: ()=>{},
        maxWidth: 400,
        maxHeight: 400,
        fileType: 'blob',
        imageType: 'image/png'
    }

    static propTypes = {
        prefixCls: PropTypes.string,
        compress: PropTypes.bool,
        className: PropTypes.string,
        style: PropTypes.object,
        onChange: PropTypes.func,
        maxWidth: PropTypes.number,
        maxHeight: PropTypes.number,
        fileType: PropTypes.oneOf(['base64', 'blob']),     //            
        imageType: PropTypes.string      //          
    }


    compress = (res) => {
        // console.log('res:', res)
        let { maxWidth, maxHeight, fileType, imageType } = this.props

        // imageType = `image/${imageType}`

        return new Promise(function (resolve, reject) {
            const img = new Image()

            img.onload = function() {
                let originWidth = this.width, originHeight = this.height

                let canvas = document.createElement('canvas');
                let context = canvas.getContext('2d');

                let targetWidth = originWidth, targetHeight = originHeight;
                //       400x400   
                if (originWidth > maxWidth || originHeight > maxHeight) {
                    if (originWidth / originHeight > maxWidth / maxHeight) {
                        //   ,        
                        targetWidth = maxWidth;
                        targetHeight = Math.round(maxWidth * (originHeight / originWidth));
                    } else {
                        targetHeight = maxHeight;
                        targetWidth = Math.round(maxHeight * (originWidth / originHeight));
                    }
                }

                // canvas       
                canvas.width = targetWidth;
                canvas.height = targetHeight;

                //     
                context.clearRect(0, 0, targetWidth, targetHeight);
                //     
                context.drawImage(img, 0, 0, targetWidth, targetHeight);

                if (fileType === 'base64') {
                    //   base64    
                    const dataUrl = canvas.toDataURL(imageType);
                    resolve(dataUrl)
                } else {
                    //  canvas   blob     
                    if (canvas.toBlob) {
                        canvas.toBlob(function(blob) {
                            resolve(blob)
                        }, imageType)
                    } else { // ios    toB
                        let data = canvas.toDataURL(imageType);
                        //dataURL      “data:image/png;base64,****”,              ,             
                        data = data.split(',')[1];
                        data = window.atob(data);
                        let ia = new Uint8Array(data.length);
                        for (let i = 0; i < data.length; i++) {
                            ia[i] = data.charCodeAt(i);
                        }
                        //canvas.toDataURL           image/png
                        let blob = new Blob([ia], {
                            type: imageType
                        });
                        resolve(blob)
                    }
                }
            }

            img.onerror = function() {
                reject(new Error('      '))
            }

            img.src = res;
        });
    }

    //         base64
    fileReader = (file) => {
        console.log('file:', file)
        return new Promise(function (resolve, reject) {
            const reader = new FileReader();

            reader.onload = (e) => {
                resolve(e.target.result)
            };

            reader.onerror = reject

            reader.readAsDataURL(file);
        })
    }

    onChange = async (e) => {
        const { onChange, compress } = this.props
        let files = e.target.files
        const len = files.length
        console.log('files:', files)
        let newFiles = []

        if (compress) {
            for(let i = 0; i < len; i++) {
                const result = await this.fileReader(files[i])
                const data = await this.compress(result)
                // console.log('data:', data)
                newFiles.push(data)
            }
        }

        console.log('newFiles:', newFiles)
        onChange && onChange({
            files,
            newFiles
        })
    }

    render() {
        const {
            prefixCls, className, children, style
        } = this.props

        const cls = classNames({
            [prefixCls]: true,
            [className]: className
        })

        return (
            <div
                className={cls}
                style={style}
                onClick={() => {
                    this.refs.inputFile.click()
                }}>
                <input ref="inputFile" type="file" multiple accept="image/*" onChange={this.onChange}/>
                { children } {/**/}
            div>
        )
    }
}

転載先:https://juejin.im/post/5c8cbee8f265da2dbb127a49