简体   繁体   中英

How can i attach more than one file with Carrierwave / AngularJs+RoR via Json?

sorry for my english.

For now i can create Task with one picture attached to it, i want more...

I have Rails on back-end and AngularJS on front-end. I create a directive which helps me send image to server via json.

app.directive 'uploadImage', ->
return{
  restrict: 'A'
  link:(scope,elem)->
    reader = new FileReader()
    reader.onload =(e)->
      scope.iFile = btoa(e.target.result)
      scope.$apply()

    elem.on 'change', ->
      scope.iFile=''
      file = elem[0].files[0]
      scope.iFilesize = file.size
      scope.iFiletype = file.type
      scope.iFilename = file.name
      scope.$apply()
      reader.readAsBinaryString(file)
}

Create task in AngularJS:

$scope.createTask =->
  $scope.task.iFile = $scope.iFile
  $scope.task.iname = $scope.iFilename
  $scope.task.itype = $scope.iFiletype
  baseTasks.post($scope.task).then (data)->
    $scope.tasks.unshift($scope.task)
    $scope.go('/tasks')
  ,(error)->
   # some code  

On the server-side i have a model Task.rb with this settings :

require 'file_size_validator' 
class Task < ActiveRecord::Base
    belongs_to :user
    belongs_to :category
    has_many :comments, as: :commentable, dependent: :destroy
    validates_presence_of :title
    validates_presence_of :text
    validates_presence_of :price
    mount_uploader :pict, ImageUploader
    validates :pict, 
    :file_size => { 
      :maximum => 0.5.megabytes.to_i 
    } 
end

and TasksController, action Create :

    def create
    params[:task][:pict] = parse_image_data(params[:iFile]) if params[:iFile]

    @task = Task.new(task_params)

    if @task.save
      clean_tempfile
      render_with_protection @task.to_json, {status: :created, location: @task }
    else
      render_with_protection @task.errors.to_json, {status: :unprocessable_entity }
    end
  end

private

def task_params
  params.require(:task).permit(:desc, :text, :price, :title, :ed, :category_id, :pict, :user_id)
end


def parse_image_data(image_data)
  Rails.logger.info 'decoding now'
  decoded_data = Base64.decode64(image_data) 
  # create 'file' understandable by Carrierwave
  @data = StringIO.new(decoded_data)
  @tempfile = Tempfile.new('task-image')
  @tempfile.binmode
  @tempfile.write decoded_data
  @tempfile.rewind

  ActionDispatch::Http::UploadedFile.new(
    :tempfile => @tempfile,
    :content_type => params[:itype],
    :filename => params[:iname]
    )
 end

 def clean_tempfile
    if @tempfile
       @tempfile.close
       @tempfile.unlink
     end
 end

Where i decoded image and attach it to model as Carrierwave need.

So please help me with attaching more than one image to my model. Thanks in advance.

You have to loop through each image in your uploadImage directive like this :

reader = new FileReader()
reader.onload = (e) ->
  scope.iFile = btoa(e.target.result)
  scope.$apply()

elem.on "change", ->
  i = 0

  while i < elem.length
    x = 0

    while x < elem[i].files.length
      file = elem[i].files[x]
      scope.iFile = ""
      scope.iFilesize = file.size
      scope.iFiletype = file.type
      scope.iFilename = file.name
      scope.$apply()
      reader.readAsBinaryString file
      x++
    i++
  return

And in your html you should have multiple true in your file input.

<input type='file' ng-mpdel='abc' multiple>

Solved. I had to create model Image with polymorphic association.So my models are :

require 'file_size_validator' 
class Image < ActiveRecord::Base
    belongs_to :imageable, polymorphic: true
    mount_uploader :picture, PictureUploader
    validates :picture, 
    :file_size => { 
      :maximum => 0.5.megabytes.to_i 
    } 
end

class Task < ActiveRecord::Base
    belongs_to :user
    belongs_to :category
    has_many :comments, as: :commentable, dependent: :destroy
    has_many :images, as: :imageable, dependent: :destroy
end

Code for upload images in a view:

<input type="file" multiple accept="image/png, image/gif, image/jpeg" upload-image/>

Create directive in angularJS with help @Nitin Verma (thank you!)

app.directive 'uploadImage', ['Restangular',(Restangular)->
    baseImages = Restangular.all('images')
    return{
    restrict: 'A'
    link: (scope, elem)->
      elem.on 'change', ->
        i = 0
        im =[]
        filD =[]
        while i < elem.length
          x = 0
          while x < elem[i].files.length
            reader = new FileReader()
            reader.onload = (e)->
              im.push(btoa(e.target.result))
              scope.$apply()
            file = elem[i].files[x]
            scope.files = elem[0].files
            scope.iFile = ""
            filD[x]=[]
            filD[x].push(file.type)
            filD[x].push(file.name)
            scope.$apply()
            reader.readAsBinaryString file
            x++
          i++
        scope.arImages = im
        scope.fileInf = filD
        return
    }
  ]

And my $scope.createTask :

$scope.createTask =->
  $scope.task.ed = $scope.edIz.name
  $scope.task.category_id = $scope.category_id.id
  $scope.task.user_id = $scope.curr.id
  baseTasks.post($scope.task).then (data)->
    $scope.taskId =data.id
    i = 0
    while i < $scope.arImages.length
      $scope.image ={}
      $scope.image.pict = $scope.arImages[i]
      $scope.image.iname = $scope.fileInf[i][1]
      $scope.image.itype = $scope.fileInf[i][0]
      $scope.image.task_id =$scope.taskId
      console.log($scope.image.task_id)
      imageRes.save( $scope.image )
      i++
    $scope.tasks.unshift($scope.task)
    $scope.go('/tasks')
  ,(error)->
    flash.error = error

I had to use $resource, because double save with Restangular is hangs the app without errors. so :

app.factory('imageRes', ['$resource', ($resource)->
    return $resource('/images/:id.json', {id: '@id'}, {
      update: {method:'PUT'},
      show: {method:'GET'},
      delete: {method:'DELETE'}

  })])

On the server side in ImagesController, action create:

def create
    params[:image][:picture] = parse_image_data(params[:pict]) if params[:pict]
    @image = Image.new(image_params)
    clean_tempfile
    if @image.save

      # render_with_protection @task.to_json, {status: :created, location: @task }
      render json: @image
    else
      render_with_protection @image.errors.to_json, {status: :unprocessable_entity }
    end
  end

Method parse_image_data is the same as in question description.

If anybody knows better way to solve this, please write it !

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM