2013-12-11 59 views
12

我是使用Rails 4操作angularjs的新手,Rails 4只提供api。我嘗試創建一個簡單的角度服務來上傳文件。但我使用Paperclip來管理文件,並且我遇到了一些問題。Rails 4 Angularjs回形針如何上傳文件

首先,我不明白如何正確收集輸入文件。我看到很多插件或胖指令來做到這一點。但我想要一個簡單的指令來收集我的文件並放入我的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" 
+0

我嘗試用簡單的指令,但是當我後我的文件回形針獲得一個錯誤回形針:: AdapterRegistry :: NoHandlerError:沒有找到處理... – Pinou

+0

你有沒有找到解決的辦法?我現在正在處理類似的情況。 – rcheuk

+0

不,我現在放棄了 – Pinou

回答

12

後故障排除了幾天,搞清楚這兩種技術的工作(我是新來的兩個-.-)怎麼樣,我設法得到的東西的工作。我不知道這是否是最好的方式,但它是有效的。如果有人有任何改進,我會很高興聽到他們。

一般情況下,我做了以下內容:

  • 在AngularJS創建一個指令來處理文件上傳
    • 編碼的文件爲Base64字符串,並將其連接到一個JSON對象。
  • 滑軌控制器解碼使用StringIO的和重新連接該文件以所述參數
    • 然後我更新或創建與新的更新後的參數的模型以base64字符串。

那感覺真是迂迴,所以如果有另一種方式來做到這一點,我想知道!

我正在使用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)); 
     }); 
    } 
    }; 
}] 
}; 
}); 

角文件與指令

<input type="file" id="fileUpload" ng-show="false" upload-image /> 
<img ng-src="{{userImageLink}}" ng-click="openFileWindow()" ng-class="{ hidden: !userImageLink}" > 
<div class="drop-box" ng-click="openFileWindow()" ng-class=" {hidden: userImageLink}"> 
    Click to add an image. 
</div> 

這將創建一個隱藏的文件輸入。 userImageLink在控制器中設置,openFileWindow()方法也一樣。如果用戶圖像存在,則會顯示,否則會顯示一個空格,告訴用戶點擊上傳圖像。

在控制器,其負責上面的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'); 
}; 

滑軌側

在用戶模型的控制器,我有以下方法:

# 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.r b文件會有這樣的:

### 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/] 

我認爲這是一切都是相關的。希望這可以幫助。當我有時間時,我可能會更清楚地將這個帖子張貼在其他地方。

+0

嗨@harmlessdragon,這個例子使用rails的angualarjs工作正常嗎?我也面臨同樣的問題。 –

+0

是的,這適用於我的應用程序使用angularjs和rails。 – rcheuk

+0

喜@harmlessdragon,我試着與你的榜樣,但我正在逐漸「$ scope.user.put()。然後(函數(結果)」沒有溫控功能。我需要在我的angularjs用戶控制器來定義這個功能呢?任何幫助? –

4

But i want juste a simple directive that collect my file and put in my ng-model

ng-file-upload只是沒有這一點,它是輕量級,易於使用的,跨瀏覽器的解決方案,支持進度/中止,拖動&拖放和預覽。

<div ng-controller="MyCtrl"> 
    <input type="file" ngf-select ng-model="files" multiple> 
</div> 

$scope.$watch('files', function(files) { 
    for (var i = 0; i < $files.length; i++) { 
     var file = $files[i]; 
     $scope.upload = $upload.upload({ 
      url: 'server/upload/url', 
      file: file, 
     }).progress(function(evt) { 
     console.log('percent: ' + parseInt(100.0 * evt.loaded/evt.total)); 
     }).success(function(data, status, headers, config) { 
     console.log(data); 
     }); 
    } 
}); 
+0

謝謝,但我得和我的強烈PARAMS一個不允許的參數。我在文件的強烈參數方面獲得了許可。 – Pinou

+0

hi @danial,我看不到你的例子中的rails控制器代碼。它會使用軌道控制器邏輯嗎? –

+0

是的,它會讓很多人使用它與軌道。它只需要有人來貢獻,並將示例代碼放在wiki中。你可以在github問題中詢問你是否有問題。 – danial