圖片上傳總結(jié)(IE9拜拜)

增加圖片發(fā)送功能

一岳守、需求

(1)點(diǎn)擊圖片 Icon,出現(xiàn)文件上傳框碌冶,選擇圖片湿痢;
(2)驗(yàn)證圖片類型及大小,本地預(yù)覽的同時(shí)上傳到服務(wù)器扑庞;
(3)上傳完之后進(jìn)行發(fā)送譬重,同消息發(fā)送(本文不涉及)。

二罐氨、DOM節(jié)點(diǎn)預(yù)備

  • 圖片Icon(展示臀规,用于點(diǎn)擊)
  • 文件上傳標(biāo)簽 <input type="file" />(隱藏)

可選項(xiàng):

  • form 表單(隱藏):如果不使用 formData,可通過(guò)表單上傳
  • iframe(隱藏):如果不使用 ajax栅隐,可用于存放圖片上傳之后返回的數(shù)據(jù)(被稱為隱藏 iframe 模擬 Ajax 跨域上傳以现,注意 form 的 target 要寫 iframe 的 name 屬性)
render() {
    return (
        <div>
            <span className="picture" onClick={this.handlePicClick}></span>
            <iframe name="post_frame" style={{display: 'none'}} onLoad={this.loadIframe}></iframe>
            <form action="/upload.action" method="post" target="post_frame" encType="multipart/form-data" ref={ node => this.imgForm= node }>
                <input type="file" 
                    name="imgUpload" 
                    accept="image/gif, image/jpeg, image/png, image/bmp"
                    onChange={this.handleImgChange} 
                    style={{display: 'none'}} 
                    ref={node => this.imgUploader = node} />
            </form>
        </div>
    );
}

三、操作實(shí)現(xiàn)

1. 點(diǎn)擊 圖片 Icon约啊,觸發(fā)文件上傳標(biāo)簽,打開文件上傳框:
handlePicClick = () => {
    this.imgUploader.click();  // 相當(dāng)于點(diǎn)擊<input type="file />佣赖,調(diào)出文件框
}
2. 監(jiān)聽 <input type="file" />onchange 事件恰矩,獲取上傳的文件內(nèi)容:
handleImgChange(e) {
    const { file, url } = this.getFile(e);  // 獲取文件內(nèi)容

    if (this.imgValidator(file)) {  // 驗(yàn)證文件內(nèi)容
        this.uploadImg(file);  // 本地預(yù)覽的同時(shí)上傳文件
    }

    // 允許多次上傳同一張圖片
    e.target.value = '';
}

【注意】:如果沒(méi)有最后一行代碼,那么在重復(fù)上傳同一張圖片時(shí)憎蛤,不會(huì)觸發(fā) onchange 事件外傅,因此需要最后一行代碼來(lái)保證能夠允許多次上傳同一張圖片纪吮,解決方案來(lái)自:圖片上傳以及允許連續(xù)上傳同一圖片

3. 獲取文件內(nèi)容(路徑,名稱萎胰,類型碾盟,大小)
getFile(e) {
    let fileEle = e.target;
    let fileObj = null;
    let filePath = '';

    if (fileEle.files) {
        fileObj = fileEle.files[0];
        filePath = window.URL.createObjectURL(fileObj);
    } else {
        // 通過(guò)網(wǎng)搜各種兼容 IE9 的方法技竟,嘗試之后冰肴,沒(méi)效果,果斷放棄了榔组。熙尉。。
        // 深深地體會(huì)到了兼容 IE9 的絕望(╥╯^╰╥)
        try {
            fileEle.select();
            fileEle.blur();
            filePath = document.selection.createRange().text;
            // filePath = fileEle.value;  // 只在本地有效
            let fileSystem = new ActiveXObject("Scripting.FileSystemObject");

            if(fileSystem.FileExists(filePath)){
                fileObj = fileSystem.GetFile(filePath);
                /* console.info("文件類型:" + fileObj.type);
                console.info("文件名稱:" + fileObj.name);
                console.info("文件大小:" + fileObj.size); */
            }
        } catch (e) {
            console.log('GetFile Error:', e);
        }
    }

    return {
        file: fileObj,
        url: filePath
    };
}

非 IE9 下可以通過(guò) e.target.files 來(lái)獲取到選擇的圖片內(nèi)容搓扯,然后生成相應(yīng)的文件路徑检痰;而 IE9 獲取不到 e.target.files 對(duì)象,需要特殊兼容下(誰(shuí)曾想兼容之路漫漫锨推,爬過(guò)一座山铅歼,又遇一道坎???????)。

(1)對(duì)象 URL:性能比較好

對(duì)象 URL 也被稱為 blob URL换可,指的是引用保存在 File 或 Blob 中數(shù)據(jù)的 URL椎椰。通過(guò) window.URL.createObjectURL(file) 方法創(chuàng)建一個(gè)對(duì)象 URL,它的返回值是一個(gè)字符串锦担,指向一塊內(nèi)存地址俭识。因?yàn)檫@個(gè)字符串是 URL,因此能夠在 DOM 中(如img標(biāo)簽)進(jìn)行使用洞渔。

使用對(duì)象 URL 的好處是可以不必把文件內(nèi)容讀取到 JavaScript 中而直接引用文件內(nèi)容套媚。頁(yè)面卸載時(shí)會(huì)自動(dòng)釋放對(duì)象 URL 占用的內(nèi)存。

fileObj 和 filePath

(2)FileReader

FileReader 實(shí)現(xiàn)的是一種異步文件讀取機(jī)制磁椒〉塘觯可以把 FileReader 想象成 XMLHttpRequest,區(qū)別只是它讀取的是文件系統(tǒng)浆熔,而不是遠(yuǎn)程服務(wù)器本辐。為了讀取文件中的數(shù)據(jù),F(xiàn)ileReader 提供了4個(gè)方法医增,這里需要用到的方法如下:

  • readAsDataURL(file):讀取文件并將文件以數(shù)據(jù) URI 的形式保存在 result 屬性中

由于讀取過(guò)程是異步的慎皱,F(xiàn)ileReader 也提供了幾個(gè)事件,包括:progress叶骨、error茫多、load

【測(cè)試代碼】:

let file = e.target.files[0];
let reader  = new FileReader();
reader.onload = (e) => {
    let url = e.target.reasult;
    // 可以構(gòu)造img對(duì)象:
    // let img = `<img src="${url}" className="upload-img" />`;
};
// 讀取file對(duì)象并存放為data: URL格式的字符串
reader.readAsDataURL(file);
e.target.result

話說(shuō)一張圖片轉(zhuǎn)成 base64 還是挺大的忽刽,不建議進(jìn)行ws傳輸天揖。
FileReader 的其它方法夺欲、 progress的信息(屬性) 和 error 事件的錯(cuò)誤碼,具體請(qǐng)參考《JavaScript高級(jí)程序設(shè)計(jì)》今膊。

(3)IE9:在假設(shè)能獲取到 input 中 text 的情況下(本地測(cè)試可以些阅,聯(lián)調(diào)環(huán)境各種問(wèn)題),so斑唬,還是別在這上面浪費(fèi)時(shí)間了╮(╯﹏╰)╭市埋,跟產(chǎn)品求個(gè)繞,果斷放棄 IE9

文件內(nèi)容
4. 驗(yàn)證文件內(nèi)容

通過(guò)步驟 3赖钞,我們可以知道上傳的圖片文件所包含的信息腰素,包括 路徑(url)、名稱(name)雪营、類型(type)弓千、大小(size)献起,因此可以對(duì)這幾項(xiàng)進(jìn)行業(yè)務(wù)上的驗(yàn)證洋访。

5. 上傳至服務(wù)器

(1)form 表單上傳

在不使用 formData 的情況下,form 表單上傳直接調(diào)用 submit 方法即可:

this.imgForm.submit();

當(dāng)數(shù)據(jù)返回之后谴餐,就是 iframe 派上用場(chǎng)的時(shí)候啦:

// 上傳圖片后返回結(jié)果處理
loadIframe(e) {
    let imgInfo = this.localInfo; // 本地圖片信息
    let isUpload;

    try {
        let response = e.target.contentDocument.body.textContent;
        if (!response) {
            isUpload = false;   // 上傳失敗
        } else {
            // 處理返回結(jié)果
            // 如果上傳成功姻政,則獲取新的 imgInfo
        }
        this.handleUploadRes(isUpload, imgInfo);  // 處理圖片結(jié)果
    } catch(e) {
        console.log('loadIframe Error:', e);    // iframe內(nèi)容讀取受限,如瀏覽器兼容性問(wèn)題
    }
}

(2) formData 上傳(IE9以下不支持)

創(chuàng)建 formData:

// 方法一:直接將 form 對(duì)象裝載到 formData 中
let fd = new FormData(this.imgFrom);

// 方法二:選擇性添加信息
let fd = new FormData();
fd.append('uploadImg', file);

發(fā)送 Ajax:

// 以下代碼來(lái)自參考文章:[上傳圖片攻略全解]岂嗓,見文末參考
let xhr = new XMLHttpRequest();
xhr.open('POST','/upload.action');
xhr.onreadystatechange = function(){};
xhr.send(fd);

// 獲取上傳的進(jìn)度
xhr.upload.onprogress = function(e){
    var loaded = e.loaded;//已經(jīng)上傳大小情況
    var tot = e.total;  //附件總大小
    var per = Math.floor(100*loaded/tot);  //已經(jīng)上傳的百分比  
}

// 或者使用框架的話:
/*ajax({
    url: '/upload.action',
    data: fd,
    type: 'post',
    success: () => {}
});*/

四汁展、補(bǔ)充

  1. 考慮到真實(shí)的業(yè)務(wù)場(chǎng)景中是需要將圖片進(jìn)行發(fā)送的,這時(shí)候需要考慮到的情況是:如果圖片過(guò)大厌殉,上傳需要一定的時(shí)間食绿,這時(shí)候就不能阻塞后續(xù)文本消息的發(fā)送。
上傳不阻塞消息發(fā)送
  1. 論寫 try...catch(e)... 的重要性公罕,在Chrome瀏覽器影響不大器紧,但是一旦調(diào)試 IE 的時(shí)候,就會(huì)明白這個(gè)語(yǔ)句有多重要了楼眷,它可以在產(chǎn)生錯(cuò)誤的時(shí)候不讓 IE 進(jìn)入調(diào)試狀態(tài)铲汪,也能直接在控制臺(tái)看到錯(cuò)誤結(jié)果,可以大大提高效率罐柳。

至此掌腰,聊天室的又一道難題解決啦~蟹蟹閱讀喲!ヾ(??▽?)ノ

五张吉、參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末士飒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蔗崎,更是在濱河造成了極大的恐慌酵幕,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缓苛,死亡現(xiàn)場(chǎng)離奇詭異芳撒,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)未桥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門笔刹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人冬耿,你說(shuō)我怎么就攤上這事舌菜。” “怎么了亦镶?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵日月,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我缤骨,道長(zhǎng)爱咬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任绊起,我火速辦了婚禮精拟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘虱歪。我一直安慰自己蜂绎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布实蔽。 她就那樣靜靜地躺著荡碾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪局装。 梳的紋絲不亂的頭發(fā)上坛吁,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音铐尚,去河邊找鬼拨脉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛宣增,可吹牛的內(nèi)容都是我干的玫膀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼爹脾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼帖旨!你這毒婦竟也來(lái)了箕昭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤解阅,失蹤者是張志新(化名)和其女友劉穎落竹,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體货抄,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡述召,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蟹地。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片积暖。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖怪与,靈堂內(nèi)的尸體忽然破棺而出夺刑,到底是詐尸還是另有隱情,我是刑警寧澤琼梆,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布性誉,位于F島的核電站,受9級(jí)特大地震影響茎杂,放射性物質(zhì)發(fā)生泄漏错览。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一煌往、第九天 我趴在偏房一處隱蔽的房頂上張望倾哺。 院中可真熱鬧,春花似錦刽脖、人聲如沸羞海。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)却邓。三九已至,卻和暖如春院水,著一層夾襖步出監(jiān)牢的瞬間腊徙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工檬某, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留撬腾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓恢恼,卻偏偏與公主長(zhǎng)得像民傻,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 如果不考慮ie9兼容性,實(shí)現(xiàn)【上傳圖片】大致的思路如下: 由于公司是將所有上傳的圖片都放到【代理服務(wù)器】里喧半。所以【...
    kakaguo閱讀 5,977評(píng)論 1 4
  • 前端無(wú)法像原生APP一樣直接操作本地文件碟刺,否則的話打開個(gè)網(wǎng)頁(yè)就能把用戶電腦上的文件偷光了,所以需要通過(guò)用戶觸發(fā)薯酝,用...
    雷波_viho閱讀 822評(píng)論 0 1
  • 參考1-HTML5實(shí)現(xiàn)圖片壓縮上傳功能參考2-移動(dòng)前端—圖片壓縮上傳實(shí)踐參考3-移動(dòng)端H5圖片壓縮上傳 大體步驟 ...
    不要變成發(fā)抖的小喵喵喵喵喵喵閱讀 3,815評(píng)論 0 15
  • 上傳文件已經(jīng)是個(gè)已經(jīng)成熟的前端技術(shù),目前開源的拿來(lái)即用的前端上傳插件也比較多爽柒,諸如:Web Uploader吴菠、JS...
    清曉凝露閱讀 11,415評(píng)論 0 22
  • 一轉(zhuǎn)眼,一個(gè)學(xué)期結(jié)束了浩村。時(shí)間快的像國(guó)內(nèi)的人流廣告一樣做葵,還沒(méi)開始,就已經(jīng)結(jié)束了心墅。這個(gè)學(xué)期是忙碌的一個(gè)學(xué)期酿矢。一邊上課,...
    Ada秦小念閱讀 208評(píng)論 0 0