После нескольких дней поиска неисправностей и выяснить, как и технологии работы (я новичок в обоих -.-), мне удалось получить что-то работать. Я не знаю, лучший ли это, но он работает. Если у кого есть какие-то улучшения, я буду рад их услышать.
В общем, я сделал следующее:
- Создать директиву в 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/]
Я думаю, что это все, что имеет значение. Надеюсь это поможет. Я, вероятно, опубликую это где-то еще немного более четко, когда у меня будет время.
Я пробую с простой директивой, но когда я отправляю свой файл paperclip, вы получите ошибку Paperclip :: AdapterRegistry :: NoHandlerError: обработчик не найден ... – Pinou
Вы когда-нибудь находили решение? Сейчас я работаю над подобной ситуацией. – rcheuk
Нет, я сдался на данный момент – Pinou