2013-12-11 5 views
12

Я начинаю манипулировать угловыми с Rails 4, которые обеспечивают только api. Я пытаюсь создать простой угловой сервис для загрузки файла. Но я использую Paperclip для управления файлом, и у меня есть некоторые проблемы.Rails 4 Angularjs Paperclip Как загрузить файл

Во-первых, я не понимаю, как правильно собрать файл ввода. Я вижу много директив плагина или жира, чтобы сделать это. Но я хочу, чтобы просто простая директива, которая собирала мой файл и помещала в мою ng-модель.

И, наконец, я хочу знать, эффективнее ли кодировать мой файл в Base64?

Моих Рельсы контроллер

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

Я пробую с простой директивой, но когда я отправляю свой файл paperclip, вы получите ошибку Paperclip :: AdapterRegistry :: NoHandlerError: обработчик не найден ... – Pinou

+0

Вы когда-нибудь находили решение? Сейчас я работаю над подобной ситуацией. – rcheuk

+0

Нет, я сдался на данный момент – Pinou

ответ

12

После нескольких дней поиска неисправностей и выяснить, как и технологии работы (я новичок в обоих -.-), мне удалось получить что-то работать. Я не знаю, лучший ли это, но он работает. Если у кого есть какие-то улучшения, я буду рад их услышать.

В общем, я сделал следующее:

  • Создать директиву в AngularJS обрабатывать файл Загрузить
    • закодированные файл как base64 строки и прикрепили его к объекту JSON.
  • Rails контроллер расшифровали base64 строку, используя StringIO и повторно прикрепил файл с параметрами
    • Затем я обновил или создал модель с новыми обновленными параметрами.

Он чувствовал себя действительно окольный, так что если есть другой способ сделать это, я хотел бы знать!

Я использую 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(). Если изображение пользователя существует, оно отображается, в противном случае он отображает пустой 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 стороны

В контроллере пользовательской модели, я бы следующее методы:

# 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

hi @harmlessdragon, этот пример отлично работает с angualarjs с использованием рельсов? Я также сталкиваюсь с такой же проблемой. –

+0

Да, это работало для моего приложения с использованием угловых и рельсов. – rcheuk

+0

hi @harmlessdragon, я попытался с вашим примером, но я получаю «$ scope.user.put(). Then (function (result)» не является fucntion. Нужно ли определять эту функцию в моем пользовательском контроллере angularjs? help? –

4

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

ng-file-upload просто делаю это, и это легкий, простой в использовании, кросс-браузерное решение, которое поддерживает прогресс/прерывание, перетащить & drop and preview.

<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

Спасибо, но я получаю параметр unpermitted с моими сильными параметрами. У меня есть разрешение в моих сильных параметрах для файла. – Pinou

+0

привет @ danial, я не вижу код контроллера рельсов в вашем примере. Будет ли он работать с логикой контроллера rails? –

+0

Да, многие люди используют его с рельсами. Ему просто нужно, чтобы кто-то внес свой вклад и разместил образец кода в wiki. Если у вас есть вопросы, вы можете спросить в вопросах github. – danial

Смежные вопросы