如何用 React 完成圖片上傳功能


編輯推薦:稀土掘金,這是一個針對技術開發者的一個應用,你可以在掘金上獲取最新最優質的技術干貨,不僅僅是Android知識、前端、后端以至於產品和設計都有涉獵,想成為全棧工程師的朋友不要錯過!

下面這篇特邀文章是由 Damon Bauer 完成的,主題是關於一個 web 開發人員非常常見的工作:為用戶提供圖片上傳功能。我想說這並不容易,但是有了一些功能強大的工具來幫忙做一些比較“重”的工作,這個任務會覺得比以前輕松許多。Damon 甚至全程在瀏覽器中完成了這項任務!

對於 web 開發者來說,讓用戶能夠上傳圖片是一件很常見的事情。一開始可能看起來小菜一碟,但是當真正創建一個圖片上傳組件的時候,還是有些問題需要去考慮的。這里有一些注意事項:

  • 允許什么類型的圖片上傳?

  • 需要多大的圖片? 這對性能有何影響?

  • 圖片長寬比例應該是多少?

  • 如何管理圖片? 能撲捉到不良圖片嗎?

  • 圖片存儲在哪? 如何運維?

諸如 Paperclip 和 ImageProcessor 這樣的服務器端工具,能解決上面大部分的問題。不幸的是,目前還沒有一個能用在單頁應用上的現成的工具。我將向你們展示我是如何在一個 React 應用中解決這個問題的,完全沒有用到服務器端語言。

這是我們將要構建的應用的一個小樣品。

工具包

我用到了下面三個工具:

設置 Cloudinary

Cloudinary 是一個可以為圖片提供存儲、操作、管理、提供功能的雲服務。我選擇使用 Cloudinary 是因為它提供的免費賬戶包含了所有我所需要的功能。你至少需要一個免費帳戶才能開始。

假如說你想裁剪,調整大小並給上傳的圖片增加濾鏡。Cloudinary 有個轉換的概念,和修改圖片功能鏈接在一塊的,不管你需不需要。一旦上傳,就會轉換、修改然后存儲新的圖片。

在 Cloudinary 控制面板中,找到 Settings > Upload,然后選擇 “Upload presets” 下方 的 “Add upload preset”。

下一步,將 “Mode” 改成 “Unsigned”。這是必須的,然后你就可以不需要使用服務器端語言來處理私鑰也能直接上傳到 Cloudinary 了。

在 “Incoming Transformations” 部分選擇 “Edit” 可以添加任何轉換。 你可以裁剪、調整大小、改變質量、旋轉、濾鏡等等。保存預設,這就行了!你現在有地方上傳、處理、存儲圖片了,能夠為你的應用程序提供圖片服務了。注意預設名稱,我們稍后將用到它。讓我們進入代碼部分吧。

接受用戶輸入

為了處理圖片上傳,我用了 react-dropzone 插件。它包含了一些功能,如拖放文件、文件類型限制和多文件上傳。

首先,安裝依賴。在命令行中輸入下面的命令,運行:

  npm install react react-dropzone superagent --save

然后在你的組件中導入 React、 react-dropzone 和 superagent。我使用 ES6 import 語法。

 

  import React from 'react';  

  import Dropzone from 'react-dropzone';

  import request from 'superagent';

 

我們稍后會用到 superagent。現在,在你的組件 render 方法中包含一個react-dropzone 實例。

  

    export default class ContactForm extends React.Component {

    render() {

      <Dropzone

        multiple={false}

        accept="image/*"

        onDrop={this.onImageDrop.bind(this)}>

        <p>Drop an image or click to select a file to upload.</p>

      </Dropzone>

  }

 

以下是這個組件的一些概要:

  • multiple={false} 同一時間只允許一個圖片上傳。

  • accept="image/*" 允許任何類型的圖片。你可以明確的限制文件類型,只允許某些類型可以上傳, 例如 accept="image/jpg,image/png"。

  • onDrop 是一個方法,當圖片被上傳的時候觸發。

當使用 React ES5 類語法(React.createClass),所有方法是 “autobound(自動綁定)” 到類實例上。這篇文章中的代碼使用 ES6 類語法(extends React.Component),不提供自動綁定的。所以我們在 onDrop 屬性中用了 .bind(this) 。(如果你不熟悉 .bind,你可以看看這篇文章了解下。)

處理拖拽圖片

現在,讓我們設置當上傳一個圖像時,做某些事情的方法。

首先,為兩條重要的上傳信息設置一個 const 。

 

  • 上傳預設 ID (當你創建了上傳預設時自動生成)
  • 你的 Cloudinary 上傳 URL

  

    // import statements

  const CLOUDINARY_UPLOAD_PRESET = 'your_upload_preset_id';

  const CLOUDINARY_UPLOAD_URL = 'https://api.cloudinary.com/v1_1/your_cloudinary_app_name/upload';

  export default class ContactForm extends React.Component {

  // render()

 

 

然后,增加一條記錄到組件初始化 state (使用 this.setState);我給這個屬性起了個名字 uploadedFileCloudinaryUrl。最終,這將存放一個上傳成功后由 Cloudinary 生成的圖片 URL。我們稍后會用到這條 state。

  

    export default class ContactForm extends React.Component { 

    constructor(props) {

      super(props);

      this.state = {

        uploadedFileCloudinaryUrl: ''

      };

    }

  }

 

react-dropzone 文檔說它總是返回一個上傳文件的數組,所以我們將該數組傳遞給 onImageDrop 方法的 files 參數。我們設置了一次只能傳一張圖片,所以圖片總是在數組的第一個位置。

調用 handleImageUpload ,將圖片(files[0])傳入該方法。我將這個方法分離出一個單獨的方法,遵循單一職責原則。從本質上講,這一原則方法教你保持方法緊湊,只做一件事。

  

    export default class ContactForm extends React.Component {

    constructor(props) { ... }

    onImageDrop(files) {

      this.setState({

        uploadedFile: files[0]

      });

       this.handleImageUpload(files[0]);

    }

     render() { ... }



  }

 

處理圖片上傳和轉換

首先,用 superagent 將我們之前設置的兩個 const POST 到 Cloudinary 。.field 方法 能讓我們將數據附加到 POST 請求中。這些數據包含了 Cloudinary 處理上傳圖片的所有信息。通過調用 .end,執行請求並提供回調。

  export default class ContactForm extends React.Component {

    constructor(props) { ... }

    onImageDrop(files) { ... }

    handleImageUpload(file) {

      let upload = request.post(CLOUDINARY_UPLOAD_URL)

        .field('upload_preset', CLOUDINARY_UPLOAD_PRESET)

        .field('file', file);

      upload.end((err, response) => {

        if (err) {

          console.error(err);

        }

        if (response.body.secure_url !== '') {

          this.setState({

            uploadedFileCloudinaryUrl: response.body.secure_url

          });

        }

      });

    }

     render() { ... }

  }

 

在 .end 回調中,打印所有返回錯誤的同時,最好也告訴用戶出現了一個錯誤。

接下來,我們接收到的響應中包含一個 URL,檢查下它是不是一個空字符串。這就是圖片被上傳,處理后 Cloudinary 生成的一個 URL。舉個例子,如果一個用戶正在編輯他的資料,上傳了一張圖片,你可以將 Cloudinary 返回的新的圖片 URL 保存到你的數據庫中。

我們目前寫的代碼,支持用戶拖拽一張圖片,組件將圖片發送到 Cloudinary,然后收到一個給我們用的轉換后的圖片 URL。

渲染階段

組件最后一部分是一個 div,可以預覽上傳后的圖片。

  export default class ContactForm extends React.Component {

    constructor(props) { ... }

    onImageDrop(files) { ... }

    handleImageUpload(file) { ... }

    render() {

      <div>

        <div className="FileUpload">

          ...

        </div>

        <div>

          {this.state.uploadedFileCloudinaryUrl === '' ? null :

          <div>

            <p>{this.state.uploadedFile.name}</p>

            <img src={this.state.uploadedFileCloudinaryUrl} />

          </div>

          }

        </div>

      </div>

    }

  }

 

如果 uploadedFileCloudinaryUrl state 是一個空字符串,三元運算符將輸出null (什么都沒有)。回想下,組件的 uploadedFileCloudinaryUrl state 默認是一個空字符串;這就意味着組件渲染時,這個 div 將是空的。

然而,當 Cloudinary 返回一個 URL,state 不再是空字符串,因為我們在handleImageUpload 更新了 state。此時,該組件將重新渲染,顯示上傳的文件名稱和變換后的圖像的預覽。

結束

This is just the groundwork for an image upload component. There are plenty of additional features you could add, like:

這只是為圖片上傳組件做的准備工作。有很多可以添加的附加功能,比如:

  • 允許多圖片上傳

  • 清除上傳的圖片

  • 如果因為某些原因上傳失敗,展示錯誤

  • 使用移動設備相機作為上傳源

目前為止,這些設置已經滿足我工作的需求了。硬編碼上傳預設不是完美的,但我還沒有碰到任何問題。

希望你們已經理解了如何不用服務器端語言,使用 React 就能上傳,存儲和操作圖片。如果你們有任何問題或者點評,我很樂意聽到你們的反饋!我已經建好了一個倉庫,你們可以點擊鏈接查看代碼.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM