[ 一起學React系列 -- 8 ] React中的文件上傳

終於抽出時間來繼續更新自己的博客,最近忙得夠嗆...
對於該系列博客不知道大家有沒有這樣的看法,對於React常見的基礎東西並沒有過多或者詳細列出,感覺有點不符合系列標題。的確,筆者一開始也想把React基礎從頭到尾列一邊,但是想想看沒這個必要,因爲這種基礎教程在網上多如牛毛,再寫豈不是重複造輪子,所以筆者就挑一些相對“偏僻”但是肯定會用到的東西拿出來分享。

前言

本期的主題是在React中如何實現文件上傳。文件上傳這個功能在實際開發過程中用的地方相對較多,當然還有很多花裏胡哨的解決方案,不過萬變不離其宗再複雜的解決方案也離不開最基礎的技術,所以筆者將文件上傳這一塊詳細整理了一下並且做了demo供大家參考學習。

文件上傳解決方案

目前比較主流的解決方案就是form表單fetch(或者axios)form表單+fetch來實現。對於第二位而言,筆者本着能用原生就用原生的原則就沒有使用axios模塊。那麼下面就一一開始分享。

文件上傳解決方案--form表單

利用表單組件進行文件上傳是遠古時期就一直在用的方法而且還真經久不衰,厲害了。利用form表單的enctype屬性可以把表單提交的對象設置爲多媒體資源,然後通過inuput:file就可以實現文件上傳的功能,例子如下:

import React, {Component} from 'react'

class FormUploadOnly extends Component {
    render() {
        return (
            <div>
                <form action="http://127.0.0.1:3001/file/upload" method="post" enctype="multipart/form-data">
                    <input type="file" name='file'/>
                    <input type="submit" value="上傳"/>
                </form>
            </div>
        )
    }
}

export default FormUploadOnly;

這個solution相對便捷有效而且還不用考慮跨域的問題,畢竟我們上傳的文件終究還是要訪問API接口;不過這種方法還有一個不方便的地方,就是form表單會默認跳轉也就是會在瀏覽器訪問你所提交文件的那個接口,這個行爲處理起來很麻煩。這個問題筆者推薦通過一個iframe來解決。

文件上傳解決方案--fetch

Fetch是瀏覽器的原生API,可以像Ajax那樣請求後臺接口。不過因爲它是基於Promise的,所以不支持Promise的瀏覽器則無法使用該方法。閒話不說,如何通過fetch來實現上傳?

import React, {Component} from 'react';

class FetchUpload extends Component {
    constructor(props) {
        super(props);
        this.fileInput = React.createRef();
    }

    upload = () => {
        const data = new FormData();
        data.append('file', this.fileInput.current.files[0]);  //相當於 input:file 中的name屬性
        fetch('http://127.0.0.1:3001/file/upload', {
            method: 'POST',
            body: data
        }).then(response => console.log(response))
    };
    render() {
        return (
            <div>
                <input type="file" name='file' ref={this.fileInput}/>
                <input type="button" value="上傳" onClick={this.upload}/>
            </div>
        )
    }
}
export default FetchUpload;

這個方法比較投機取巧,就是將input:type中的數據append到FormData中,FormData會將數據編譯成鍵值對,這樣可以被fetch發送至後臺(不僅僅限於fetch,也可以是ajax或者axios等等)。不過這種方法有個致命的問題,那就是會有跨域問題。對於這個問題,筆者會在博客末尾提供相關解決方案。

文件上傳解決方案--fetch+form

這個方案看小標題和前面的內容,相信大家都能猜到是什麼樣子了。下面直接上代碼:

import React, {Component} from 'react'

class FormUpload extends Component {
    submit = (e) => {
        e.preventDefault();
        let formData = new FormData(e.target);
        fetch('http://127.0.0.1:3001/file/upload', {
            method: 'POST',
            body: formData //自動將input:file的name屬性與文件對象組合成鍵值對
        }).then(response => console.log(response))
    };

    render() {
        return (
            <div>
                <form onSubmit={this.submit}>
                    <input type="file" name='file'/>
                    <input type="submit" value="上傳"/>
                </form>
            </div>
        )
    }
}

export default FormUpload;

總的來說,這個方法和第二中方法在原理上是相同的,只是獲取的文件數據不是直接從input:type中獲取的,而是從form的提交事件中獲取的,其他的沒什麼變化,所以也會遇到跨域的問題。

後臺組成

該博客的demo後臺是express寫的,所以不管是跨域管理還是接收並保存文件都是基於Node模塊。

跨域管理

筆者常用的Node服務跨域解決方案是第三方庫cors。當然cors除了是這個第三方庫的名字,也有比較重要的W3C標準,它對解決瀏覽器跨域問題起到重要的作用,不過該博文的重點不在這所以不作贅述,相關的使用方法都在文末的demo裏,有興趣的朋友可以嘗試用用,真的很high!

接收並保存文件

因爲express自身的request對象不包含上傳過來的文件對象,所以必須要用到第三方庫multer。負責處理multipart/form-data 類型的表單數據和保存相關資源的作用。

小提示

利用multer初始化一個upload中間件對象時候需要指定一個“標誌符”,比如:

let upload = multer({storage: storage}).single('file');

這裏的標識符是file,對應前面代碼中的:

<input type="file" name='file'/>

data.append('file', this.fileInput.current.files[0])

這是一個不算大的坑,所以大家使用時候多多關注。

最後筆者奉上準備好的demo,有興趣的話可以download下來耍耍。當然裏面還包含了下一篇文件下載的代碼,大家可以也順帶看看。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章