首页 > 解决方案 > 如何编写用于正确下载和下载文件的控制器

问题描述

我有 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

标签: ruby-on-rails

解决方案


TL;博士:

除非您真的打算将文件数据包含在 JSON 响应中,否则我将使用解决方案 2 而不是解决方案 1。

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

应用程序/模型/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 字符串

应用程序/控制器/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

应用程序/配置/路由.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)
});

推荐阅读