如何编写用于正确下载和下载文件的控制器

时间:2018-07-16 08:45:19

标签: ruby-on-rails

我将Rails作为后端和Angular 5的前端。如何在Rails上正确编写控制器和路由,以便将来可以使用Angular上传和下载文件? 我正在使用的常规控制器不起作用:

class FilesController < ApplicationController
  before_action :set_file, only: [:show, :update, :destroy]

  def index
    @files = File.all

    render json: @files
  end

def show
  render json: @file
end

def create
  @file = File.new(files_params)

  if @file.save
    render json: @file, status: :created, location: @file
  else
    render json: @file.errors, status: :unprocessable_entity
  end
end

def update
  if @file.update(files_params)
    render json: @file
  else
    render json: @file.errors, status: :unprocessable_entity
  end  
end

def destroy
  @file.destroy
end

private
  def set_file
    @file = File.find(params[:id])
  end

  def files_params

    params.require(:file).permit!
  end
end

文件:

ID:          NUMBER
NAME:        VARCHAR2
FILE_NAME:   VARCHAR2
CONTENT:     BLOB
FILE_TYPE:   VARCHAR2

1 个答案:

答案 0 :(得分:0)

TL; DR:

除非您确实打算将File-data作为JSON响应的一部分,否则我将使用解决方案2代替解决方案1。

解决方案1:JSON包含文件的Base64编码的字符串

app / models / file.rb:

class File < ApplicationRecord
  def as_json(options = nil)
    super(
      except: [:content],
      methods: [:content_encoded]
    )
  end

  def content_encoded
    Base64.strict_encode64(content)
  end
end

然后,在您的JS中:

// do AJAX call to FilesController#index...
// sample JSON response:
// [
//   { "id": 1, "name": "...", "file_name": "...", "file_type": "...", "content_encoded": "SGVsbG8gCiB3b3JsZCBoYWhhaGE=", "created_at": "...", "updated_at": "..." },
//   { "id": 2, "name": "...", "file_name": "...", "file_type": "...", "content_encoded": "WW93cyB5b3dzIHlvd3Mh", "created_at": "...", "updated_at": "..." }
// ]
// then, say we now have this response body Object (named `data`)
Object.forEach(data, function(file) {
  // atob decodes Base64-encoded-string into Binary-string
  var fileContent = atob(file.content_encoded)
});

解决方案2:JSON包含文件的URL字符串

app / controllers / files_controller.rb:

class FilesController < ApplicationController
  before_action :set_file, only: [:show, :update, :destroy, :content]

  def index
    @files = File.all

    json_response = @files.collect do |file|
      file.as_json(
        except: :content
      ).merge(
        content_url: content_file_url(file)
      )
    end

    render json: json_response
  end

  def content
    send_data @file.content
  end

  # ...
end

app / config / routes.rb

Rails.application.routes.draw do
  resources :files do
    # will generate a route: GET /files/:id/content as `content_file_url`
    get :content, on: :member
  end
end

然后,在您的JS中

// do AJAX call to FilesController#index...
// sample JSON response:
// [
//   { "id": 1, "name": "...", "file_name": "...", "file_type": "...", "content_url": "http://localhost:3000/files/1/content", "created_at": "...", "updated_at": "..." },
//   { "id": 2, "name": "...", "file_name": "...", "file_type": "...", "content_url": "http://localhost:3000/files/2/content", "created_at": "...", "updated_at": "..." }
// ]
// then, say we now have the response body Object (named `data`)
Object.forEach(data, function(file) {
  console.log(file.content_url)
});