Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Rails JSON file upload with carrierwave (from base64 string)
class Api::UploadsController < ApiController
def create
@upload = Upload.new(upload_params)
ensure
clean_tempfile
end
private
def upload_params
the_params = params.require(:upload).permit(:file)
the_params[:file] = parse_image_data(the_params[:file]) if the_params[:file]
the_params
end
def parse_image_data(base64_image)
filename = "upload-image"
in_content_type, encoding, string = base64_image.split(/[:;,]/)[1..3]
@tempfile = Tempfile.new(filename)
@tempfile.binmode
@tempfile.write Base64.decode64(string)
@tempfile.rewind
# for security we want the actual content type, not just what was passed in
content_type = `file --mime -b #{@tempfile.path}`.split(";")[0]
# we will also add the extension ourselves based on the above
# if it's not gif/jpeg/png, it will fail the validation in the upload model
extension = content_type.match(/gif|jpeg|png/).to_s
filename += ".#{extension}" if extension
ActionDispatch::Http::UploadedFile.new({
tempfile: @tempfile,
content_type: content_type,
filename: filename
})
end
def clean_tempfile
if @tempfile
@tempfile.close
@tempfile.unlink
end
end
end
@ifightcrime

This comment has been minimized.

@ifightcrime

This comment has been minimized.

Copy link
Owner Author

@ifightcrime ifightcrime commented Jun 17, 2014

This takes the place of what Rack normally does automatically for file uploads. We explicitly create our own instance of ActionDispatch::Http::UploadedFile from the base64 string which is what carrierwave expects.

@melcher

This comment has been minimized.

Copy link

@melcher melcher commented Feb 11, 2015

This idea was turned into a gem as well https://github.com/lebedev-yury/carrierwave-base64

@Mohakjuneja

This comment has been minimized.

Copy link

@Mohakjuneja Mohakjuneja commented Feb 27, 2016

@ifightcrime In what format should we send the file data in the JSON?
For instance my JSON looks like-
{ "content": { "asset": "data:application/pdf;base64,(L2hvbWUvbW9qby9Eb3dubG9hZHMvYm9va2UucGRm)" } }

Is this correct?

@Mohakjuneja

This comment has been minimized.

Copy link

@Mohakjuneja Mohakjuneja commented Mar 4, 2016

Hey @ifightcrime I was able to make it work by changing line 23 of code
@tempfile.write Base64.decode64(string) to this
@tempfile.write File.open(Base64.decode64(string)).read
Is this approach correct?

@txssseal

This comment has been minimized.

Copy link

@txssseal txssseal commented Apr 7, 2016

👍

@adamcrown

This comment has been minimized.

Copy link

@adamcrown adamcrown commented Oct 3, 2016

FYI, ActionDispatch::Http::UploadedFile expects type not content_type.

ActionDispatch::Http::UploadedFile.new({
  tempfile: @tempfile,
  type: content_type,
  filename: filename
})

Seems weird, I know. But it is what it is.

Thanks, by the way. This was really helpful.

@trantorLiu

This comment has been minimized.

Copy link

@trantorLiu trantorLiu commented Apr 18, 2017

@adamcrown Thank you for the useful info :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment