我是新手用Rails 4操纵angularjs,它只提供api.我尝试创建一个简单的角度服务来上传文件.但我使用Paperclip来管理文件,我有一些问题.
首先,我不明白如何正确收集输入文件.我已经看到很多插件或胖指令来做到这一点.但我想要一个简单的指令收集我的文件并放入我的ng模型.
最后我想知道在Base64中编码我的文件是否更有效?
我的Rails控制器
class Api::EmployeesController < Api::BaseController
def create
employee = Employee.create(employee_params)
if employee.save
render json: employee
else
render :json => { :errors => employee.errors.full_messages }, :status => 406
end
end
def employee_params
params.require(:employee).permit(:first_name,:mobile_phone,:file)
end
end
我的Angularjs服务
angular.module('test').factory 'Employee', ($resource, $http) ->
class Employee
constructor: (errorHandler) ->
@service = $resource('/api/employees/:id',
{id: '@id'},
{update: {method: 'PATCH'}})
@errorHandler = errorHandler
create: (attrs, $scope) ->
new @service(employee: attrs).$save ((employee) ->
$scope.employees.push(employee)
$scope.success = true
$timeout (->
$scope.success = false
), 3000
), @errorHandler
我的Angularjs控制器
angular.module('test').controller "EmployeesController", ($scope, $timeout, $routeParams, $location, Employee) ->
$scope.init = ->
@employeeService = new Employee(serverErrorHandler)
$scope.employees = @employeeService.all($scope)
$scope.createEmployee = (employee) ->
if $scope.employeeFirstName
@employeeService.create (
first_name: $scope.employeeFirstName
last_name: $scope.employeeLastName
promotion: $scope.employeePromotion
mobile_phone: $scope.employeeMobilePhone
nationality: $scope.employeeNationality
social_number: $scope.employeeSocialNumber
born_place: $scope.employeeBornPlace
employee_convention: $scope.employeeConvention
employee_type: $scope.employeeType
), $scope
else
$scope.error = "fields missing"
rcheuk.. 13
经过几天的故障排除并弄清楚这两种技术是如何工作的(我对两者都不熟悉),我设法让事情变得有效.我不知道这是不是最好的方式,但它确实有效.如果有人有任何改进,我会很高兴听到他们.
一般来说,我做了以下事情:
在AngularJS中创建一个指令来处理文件上载
将文件编码为base64 String并将其附加到JSON对象.
Rails控制器使用StringIO解码base64 String并将文件重新附加到参数
然后我使用新的更新参数更新或创建了模型.
它感觉真的很迂回,所以如果有另一种方法可以做到这一点,我想知道!
我正在使用Rails 4和AngularJS,Paperclip和Restangular的最新稳定版本.
这是相关的代码:
Angularjs指令
var baseUrl = 'http localhost:port'; // fill in as needed angular.module('uploadFile', ['Restangular']) // using restangular is optional .directive('uploadImage', function () { return { restrict: 'A', link: function (scope, elem, attrs) { var reader = new FileReader(); reader.onload = function (e) { // retrieves the image data from the reader.readAsBinaryString method and stores as data // calls the uploadImage method, which does a post or put request to server scope.user.imageData = btoa(e.target.result); scope.uploadImage(scope.user.imagePath); // updates scope scope.$apply(); }; // listens on change event elem.on('change', function() { console.log('entered change function'); var file = elem[0].files[0]; // gathers file data (filename and type) to send in json scope.user.imageContent = file.type; scope.user.imagePath = file.name; // updates scope; not sure if this is needed here, I can not remember with the testing I did...and I do not quite understand the apply method that well, as I have read limited documentation on it. scope.$apply(); // converts file to binary string reader.readAsBinaryString(file); }); }, // not sure where the restangular dependency is needed. This is in my code from troubleshooting scope issues before, it may not be needed in all locations. will have to reevaluate when I have time to clean up code. // Restangular is a nice module for handling REST transactions in angular. It is certainly optional, but it was used in my project. controller: ['$scope', 'Restangular', function($scope, Restangular){ $scope.uploadImage = function (path) { // if updating user if ($scope.user.id) { // do put request $scope.user.put().then( function (result) { // create image link (rails returns the url location of the file; depending on your application config, you may not need baseurl) $scope.userImageLink = baseUrl + result.image_url; }, function (error) { console.log('errors', JSON.stringify(errors)); }); } else { // if user does not exist, create user with image Restangular.all('users') .post({user: $scope.user}) .then(function (response) { console.log('Success!!!'); }, function(error) { console.log('errors', JSON.stringify(errors)); }); } }; }] }; });
带指令的角度文件
Click to add an image.
这会创建一个隐藏文件输入.所述userImageLink
被设定在控制器中,因为是openFileWindow()
方法.如果存在用户图像,则显示该图像,否则显示空白div,告知用户单击以上载图像.
在负责上面的html代码的控制器中,我有以下方法:
// triggers click event for input file, causing the file selection window to open
$scope.openFileWindow = function () {
angular.element( document.querySelector( '#fileUpload' ) ).trigger('click');
console.log('triggering click');
};
Rails Side
在用户模型的控制器中,我有以下方法:
# set user params
before_action :user_params, only: [:show, :create, :update, :destroy]
def create
# if there is an image, process image before save
if params[:imageData]
decode_image
end
@user = User.new(@up)
if @user.save
render json: @user
else
render json: @user.errors, status: :unprocessable_entity
Rails.logger.info @user.errors
end
end
def update
# if there is an image, process image before save
if params[:imageData]
decode_image
end
if @user.update(@up)
render json: @user
else
render json: @user.errors, status: :unprocessable_entity
end
end
private
def user_params
@up = params.permit(:userIcon, :whateverElseIsPermittedForYourModel)
end
def decode_image
# decode base64 string
Rails.logger.info 'decoding now'
decoded_data = Base64.decode64(params[:imageData]) # json parameter set in directive scope
# create 'file' understandable by Paperclip
data = StringIO.new(decoded_data)
data.class_eval do
attr_accessor :content_type, :original_filename
end
# set file properties
data.content_type = params[:imageContent] # json parameter set in directive scope
data.original_filename = params[:imagePath] # json parameter set in directive scope
# update hash, I had to set @up to persist the hash so I can pass it for saving
# since set_params returns a new hash everytime it is called (and must be used to explicitly list which params are allowed otherwise it throws an exception)
@up[:userIcon] = data # user Icon is the model attribute that i defined as an attachment using paperclip generator
end
user.rb文件将具有:
### image validation functions
has_attached_file :userIcon, styles: {thumb: "100x100#"}
#validates :userIcon, :attachment_presence => true
validates_attachment :userIcon, :content_type => { :content_type => ["image/jpg", "image/gif", "image/png"] }
validates_attachment_file_name :userIcon, :matches => [/png\Z/, /jpe?g\Z/]
我认为这是一切相关的东西.希望这可以帮助.当我有时间时,我可能会在其他地方更清楚地发布这个.