zoukankan      html  css  js  c++  java
  • 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";
    • 1
    • 2
    <input className="form-control" name={form_input_name} id={form_input_id} ref="urlinput" value={this.state.url || ""} onChange={this.handleUrlChange}/>
    • 1
    • 二,使用<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>
    • 1
    • 2
    • 3
    .image-input-btn {
        float: left;
         15%;
        background-image: image-url("upload.png");
        background-size: 30px;
        background-position: center;
        background-repeat: no-repeat;
        input {
          //hide file input button
           inherit;
          opacity: 0;
        }
      }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    接下来看看后端的代码:

    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
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    代码同样简单,构建文件路径,保存文件,返回路径。因为开发进度的原因这里并没有对图片的大小和类型进行修改(为了减少数据传输量,在前端进行大小的修改比较合理),后面review这部分代码的时候会再更新这篇博客。

    值得说说的是:

    • 图片命名的问题,采用了时间戳,命名不会重复,为了方便管理,将不同类型的图片存于不同的文件夹,但是只以时间戳命名可获取的信息太少,不利于运营人员管理,后期会再仔细考虑这个问题。
    • 存放路径问题,在后端代码里,rails能将图片存在rails项目文件夹的任何位置,但是客户端显示图片的时候,只能访问public文件夹里的内容,于是决定将图片存在public文件夹下。

    三,总结及思考

    完成这个功能不需要太久,但代码外的思考不少。图片小,甚至能用Git备份图片,也许不是长久之计,开始想念aws的好了,容灾交给aws处理最好不过了,后面试了一下AWS S3

    希望自己写得越多,考虑得越远。

    在家的第十天,日子过得还是那么快,终于还是有点慌。接触的东西越多,要学的就越多,兴奋之余隐隐感觉时间不够用。既然梦想着成为一名全栈工程师,就理应付出更多。

  • 相关阅读:
    Pig Latin-freecodecamp算法题目
    Search and Replace -freecodecamp算法题目
    Where art thou-freecodecamp算法题目
    Roman Numeral Converter-freecodecamp算法题目
    Diff Two Arrays-freecodecamp算法题目
    Asp.Net前台调用后台变量
    ASP.NET获取前端页面的Html标签的值
    echart 设置图例图标形状
    解决tableexport导出到excel中有关中文乱码的问题
    C# Async与Await用法
  • 原文地址:https://www.cnblogs.com/goody9807/p/6064635.html
Copyright © 2011-2022 走看看