ruby on rails爬坑(三):圖片上傳及顯示

一,問題及思路

最近在用rails + react + mysql基本框架寫一個cms + client的項目,裏面涉及到了圖片的上傳及顯示,下面簡單說說思路,至於這個項目的配置部署,應該會在寒假結束總結分享一下。

rails中圖片上傳及顯示要解決主要問題是:

  • 圖片存在哪?
  • 圖片格式大小?
  • 客戶端怎麼顯示圖片?

因爲這是個小項目,估計最多1000張圖片,最多佔用空間1G,所以採取相對簡便的方法:圖片保存在rails的public文件夾裏(也就是保存在部署該項目的主機中),如果圖片比較多的話,還是推薦用亞馬遜雲提供的服務AWS S3(理解爲一個硬盤,S3提供了接口給你存取東西,安全,管理方便)。

大概的思路是,前端通過<input type="file"/>選擇文件,發送ajax請求到後端的controller,controller將請求的圖片數據進行大小裁剪、類型轉換後保存到本地指定的文件夾,同時將路徑返回,用於顯示圖片。

二,實踐

思路比較簡單,所以話不多說,直接上代碼:

前端代碼整合在react寫的一個圖片上傳組件裏,image_uploader.js.jsx代碼如下

var ImageUploader = React.createClass({
  getInitialState: function() {
    return {
      url: this.props.url
    };
  },
  onFileSelect: function(e) {
    var that = this;
    var files = e.target.files;
    if (files.length <= 0) {
      AlertModal.showWithProps("No file selected");
      return;
    }
    var data = new FormData();
    $.each(files, function(key, value) {
      data.append('file', value);
      data.append('type', that.props.type)
    });
    this.upload(data);
  },
  upload: function(data) {
    var that = this;
    if (!data) {
      return;
    } else {
      this.refs.filebtn.disabled = true;
      $("#loading-modal").modal('show');
      $.ajax({
        url: '/missions/upload_image',
        type: 'post',
        data: data,
        processData: false,
        contentType: false
      }).done(function(res){
        console.log(res);
        that.setState({url: res.url});        
      }).fail(function(err){
        console.log(err);
        AlertModal.showWithProps("Upload Failed");
      }).always(function(){
        $("#loading-modal").modal('hide');
        that.refs.filebtn.disabled = false;
      });
    }
  },
  handleUrlChange: function(e) {
    this.setState({url: e.target.value});
  },
  render: function() {
    var form_input_name = this.props.model + "[thumb]";
    var form_input_id = this.props.model+ "_thumb";
    return (
      <div className="image-uploader-inputs row">
        <div className="col-sm-8 image-input">
          <input className="form-control" name={form_input_name} id={form_input_id} ref="urlinput" value={this.state.url || ""} onChange={this.handleUrlChange}/>
        </div>
        <div className="btn btn-primary btn-file image-input-btn">
          <input type="file" onChange={this.onFileSelect} ref="filebtn"/>
        </div>
      </div>
    );
  }
});

效果圖
上面的重點在於upload函數,源碼是最好的文檔,如果看源碼需要太多註釋的話,那肯定是我寫的代碼質量還不夠高,請批評指出。

寫這個的時候遇到兩個問題:

  • 一,這個組件是用在_form.html.slim裏的,這個表單是用於信息的錄入的,大家對錶單應該比較熟悉,既然要用在表單裏,就是給這個組件作標識,標明name和id,代碼片段如下(從上面的代碼中截取):
var form_input_name = this.props.model + "[thumb]";
var form_input_id = this.props.model+ "_thumb";
<input className="form-control" name={form_input_name} id={form_input_id} ref="urlinput" value={this.state.url || ""} onChange={this.handleUrlChange}/>
  • 二,使用<input type="file"/>有一個普遍的問題,自帶的UI不美觀。
    自帶的UI
    谷歌/百度“input file btn”會有許多解決方案,家裏網速差就沒細看。我的做法是把這個btn設爲透明,相關的代碼及css如下:
<div className="btn btn-primary btn-file image-input-btn">
  <input type="file" onChange={this.onFileSelect} ref="filebtn"/>
</div>
.image-input-btn {
    float: left;
    width: 15%;
    background-image: image-url("upload.png");
    background-size: 30px;
    background-position: center;
    background-repeat: no-repeat;
    input {
      //hide file input button
      width: inherit;
      opacity: 0;
    }
  }

接下來看看後端的代碼:

def upload_image
    # => will resize later
    image_relative_path = "/assets/images/#{params[:type]}/#{Time.now.to_i}.png"
    image_path = File.expand_path(File.dirname(__FILE__) + '/../..') + "/public" + image_relative_path

    data = File.read(params[:file].path)
    img = File.new(image_path, "w+")
      if img
        img.syswrite(data)
      end
    img.close
    render json: {url: image_relative_path}
 end

代碼同樣簡單,構建文件路徑,保存文件,返回路徑。因爲開發進度的原因這裏並沒有對圖片的大小和類型進行修改(爲了減少數據傳輸量,在前端進行大小的修改比較合理),後面review這部分代碼的時候會再更新這篇博客。

值得說說的是:

  • 圖片命名的問題,採用了時間戳,命名不會重複,爲了方便管理,將不同類型的圖片存於不同的文件夾,但是隻以時間戳命名可獲取的信息太少,不利於運營人員管理,後期會再仔細考慮這個問題。
  • 存放路徑問題,在後端代碼裏,rails能將圖片存在rails項目文件夾的任何位置,但是客戶端顯示圖片的時候,只能訪問public文件夾裏的內容,於是決定將圖片存在public文件夾下。

三,總結及思考

完成這個功能不需要太久,但代碼外的思考不少。圖片小,甚至能用Git備份圖片,也許不是長久之計,開始想念aws的好了,容災交給aws處理最好不過了,後面試了一下AWS S3: ruby on rails爬坑(四):使用AWS S3存取數據

希望自己寫得越多,考慮得越遠。

在家的第十天,日子過得還是那麼快,終於還是有點慌。接觸的東西越多,要學的就越多,興奮之餘隱隱感覺時間不夠用。既然夢想着成爲一名全棧工程師,就理應付出更多。

學一點,消化一點。
保持對所有技術的敬重和熱愛。

上面算是自勉吧,加油,也祝寫了一天代碼的自己情人節快樂。

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