Base64 Encode Image and Save it later to a file - ruby-on-rails

I have an application where an admin can upload an image. I save the image in a file and also base64 encode (using Base64.strict_encode method of ruby) & save in my DB. This is so that when later someone deleted the physical file from the HDD/Server, I can still generate it back by base64 decoding it (Base64.decode method) and save in a file.
But the encoding and decoding didn't go well as the image get damaged and I'm unable to view it after save.
I checked the output of the Base64.strict_encode against the result when I used http://www.base64-image.de/ to encode the file, they were different.
Can anyone help me with this? What am I doing wrong? What am I not doing?
ENCODING THE IMAGE DURING UPLOAD:
imageLoc = image.image.to_s
logger.info '>>>>>>' + (Base64.strict_encode64(open(imageLoc).read)).to_s
image_data = Base64.strict_encode64(File.open(imageLoc, 'rb').read)
CategoryImage.update_image_data(image.id,image_data)
DECODING WHEN IMAGE FILE IS LOST:
File.open(File.join(APP_CONFIG['image_storage_location'], image[:image]), 'wb') { |f|
content = image[:image_data]
content.gsub!('\\r', "\r")
content.gsub!('\\n', "\n")
f.write(Base64.decode64(content))
f.close
}
ENCODED IMAGE FROM THE SITE (base64-image.de): https://shrib.com/cYLKfEe1?v=nc
ENCODED IMAGE FROM MY CODE: https://shrib.com/CODE-encoded%20image?v=nc
EDIT
When I replaced the encoded image data in my DB with the one I generated from the above named website, my image was regenerated and viewable. So the real is with the encoding.

Had once a similar issue, solved it by replacing the File.read method with IO.binread(imageLoc). Hope it helps. :)

Related

Ruby: Is there a way to specify your encoding in File.write?

TL;DR
How would I specify the mode of encoding on File.write, or how would one save image binary to a file in a similar fashion?
More Details
I'm trying to download an image from a Trello card and then upload that image to S3 so it has an accessible URL. I have been able to download the image from Trello as binary (I believe it is some form of binary), but I have been having issues saving this as a .jpeg using File.write. Every time I attempt that, it gives me this error in my Rails console:
Encoding::UndefinedConversionError: "\xFF" from ASCII-8BIT to UTF-8
from /app/app/services/customer_order_status_notifier/card.rb:181:in `write'
And here is the code that triggers that:
def trello_pics
#trello_pics ||=
card.attachments.last(config_pics_number)&.map(&:url).map do |url|
binary = Faraday.get(url, nil, {'Authorization' => "OAuth oauth_consumer_key=\"#{ENV['TRELLO_PUBLIC_KEY']}\", oauth_token=\"#{ENV['TRELLO_TOKEN']}\""}).body
File.write(FILE_LOCATION, binary) # doesn't work
run_me
end
end
So I figure this must be an issue with the way that File.write converts the input into a file. Is there a way to specify encoding?
AFIK you can't do it at the time of performing the write, but you can do it at the time of creating the File object; here an example of UTF8 encoding:
File.open(FILE_LOCATION, "w:UTF-8") do
|f|
f.write(....)
end
Another possibility would be to use the external_encoding option:
File.open(FILE_LOCATION, "w", external_encoding: Encoding::UTF_8)
Of course this assumes that the data which is written, is a String. If you have (packed) binary data, you would use "wb" for openeing the file, and syswrite instead of write to write the data to the file.
UPDATE As engineersmnky points out in a comment, the arguments for the encoding can also be passed as parameter to the write method itself, for instance
IO::write(FILE_LOCATION, data_to_write, external_encoding: Encoding::UTF_8)

How to open base64 spreadsheet on Ruby

I've been trying to manipulate a file that's base64 encoded that I'm recieving from my client.
I'm currently using https://github.com/zdavatz/spreadsheet/blob/master/GUIDE.md to manipulate it, however, there doesn't appear to be any way to open a file directly from the base64 blob, or should I write it and then read from it? can't that a potential security threat for the server?
for example, if I recieve a file :
file = params[:file] with contents:
data:application/vnd.ms-excel;base64,0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAOwADAP7
(should I remove the data:application/vnd.ms-excel;base64, ?)
I'd like to open it with this:
Spreadsheet.client_encoding = 'UTF-8'
book = Spreadsheet.open "#{Rails.root}/app/assets/spreadsheet/event.xls"
(or with a blob or temp fle)
Sorry if it's pretty obvious, been looking for hours and there's not much info about it available, tried creating a temp file first but I don't think that's supported and there's not much I can get from the docs.
Shot in the dark: Maybe decode it, write to binary-enabled tempfile, and then feed that to Spreadsheet?
tmpfile = Tempfile.new.binmode
tmpfile << Base64.decode64(params[:file])
tmpfile.rewind
book = Spreadsheet.open(tmpfile)

Is there a way to convert binary data into a data type that will allow ActiveStorage to attach it as an image to my User model

I am hitting an api to get an image that they have stored and use it as the profile pic for our application's users. I'm using Ruby on Rails and ActiveStorage with AWS to attach and store the image. What they send back is this:
{"status"=>"shared", "values"=>[{"$objectType"=>"BinaryData", "data"=>"/9j/4AAQSkZJRgABAQAASABIAAD/4QBMRXhpZgAAT.....KK5tT/9k=", "mime_type"=>"image/jpeg", "metadata"=>{"cropped"=>false}}]}
I tried a lot of different ways to attach it and manipulate the data such as just attaching it as it is, Base64.decode64, Base64.encode64. I also tried creating a new file and then attaching that. Here are some examples:
data = Base64.decode64(Base64.encode64(response[:selfie_image]["values"][0]["data"]))
user.profile_pic.attach!(
io: StringIO.new(open(data).read),
filename: "#{user.first_name}_#{user.last_name}_#{user.id}.jpg",
content_type: "image/jpeg"
)
data = Base64.decode64(Base64.encode64(response[:selfie_image]["values"][0]["data"]))
out_file = File.new("#{user.first_name}_#{user.last_name}_# . {user.id}.jpg", "w")
out_file.puts(data)
out_file.close
user.profile_pic.attach(
io: StringIO.new(open(out_file).read),
filename: "#{user.first_name}_#{user.last_name}_#{user.id}.jpg",
content_type: "image/jpeg"
)
I also tried:
user.profile_pic.attach(out_file)
It keeps either saying attachment is nil or depending on how I manipulate the data it will say not a jpeg file content header type wrong and throw that as an image magick error.
How can I manipulate this data to be able to attach it as an image for our users with ActiveStorage?
To get this to work I had to add gem "mini_magick" to my gemfile and then use it to decode the image data I was receiving from the api call and then turn it into a blob so that ActiveStorage could handle it.
data = response[:selfie_image]["values"][0]["data"]
decoded_data = Base64.decode64(data)
image = MiniMagick::Image.read(decoded_data)
image.format("png")
user.profile_pic.attach(
io: StringIO.new(image.to_blob),
filename: "#{user.id}_#{user.first_name}_#{user.last_name}.png",
content_type: "image/jpeg"
)
In command line ImageMagick you can use the inline: feature to decode base64 data into a gif or jpg. The base64 image here has transparency, it is most proper to save to gif or png.
convert 'inline:data:image/gif;base64,
R0lGODlhIAAgAPIEAAAAAB6Q/76+vvXes////wAAAAAAAAAAACH5BAEAAAUALAAA
AAAgACAAAAOBWLrc/jDKCYG1NBcwegeaxHkeGD4j+Z1OWl4Yu6mAYAu1ebpwL/OE
YCDA0YWAQuJqRwsSeEyaRTUwTlxUqjUymmZpmeI3u62Mv+XWmUzBrpeit7YtB1/r
pTAefv942UcXVX9+MjNVfheGCl18i4ddjwwpPjEslFKDUWeRGj2fnw0JADs=
' b64_noseguy.gif
But you can still save it to jpg. The transparency will be lost and the background will be black.
convert 'inline:data:image/jpeg;base64,
R0lGODlhIAAgAPIEAAAAAB6Q/76+vvXes////wAAAAAAAAAAACH5BAEAAAUALAAA
AAAgACAAAAOBWLrc/jDKCYG1NBcwegeaxHkeGD4j+Z1OWl4Yu6mAYAu1ebpwL/OE
YCDA0YWAQuJqRwsSeEyaRTUwTlxUqjUymmZpmeI3u62Mv+XWmUzBrpeit7YtB1/r
pTAefv942UcXVX9+MjNVfheGCl18i4ddjwwpPjEslFKDUWeRGj2fnw0JADs=
' b64_noseguy.jpg
See https://imagemagick.org/Usage/files/#inline
Sorry, I do not know the equivalent in RMagick

Rails save file as image

I use webdav protocol. gem 'net_dav' it's old but usefull
when i get file through it i receive it like this \xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01\x01\x01\x00H\x00H\x00\x00\xFF\...
How to convert to real image .jpg or .png?
you will have to decode this base64 string and then save file, something like
File.open('file_name.jpg', 'wb') do|f|
f.write(Base64.decode64(base_64_encoded_string))
end

Rails/Ruby save image as base64 and access it in the views

I would like to know can we convert a image into base64 and save it in a database and access it in the views.
I have searched google and stackoverflow, all of them starts from middle like encoding or displaying the image.
I need to know how can we convert a image url/path(lets say i store image inside my app and its url stored in column)
How to encode it as base64 before saving(should we convert to base64 first and save in db?).
How to display it in the views
You can split this task to three or four steps:
getting the image
encoding to base64
storing it in database (optionaly)
display it in views
Getting the image
From Assets pipeline
If you are using Rails asset pipeline for that, you can use Rails.application.assets hash to get to image: Rails.application.assets['image_name.png'].to_s will give you the content of image_name.png image.
from file - local or by url
Here is the question about that on StackOverflow.
encode
Base64 Ruby module docs tells how to use Base64 encoding in Ruby:
Base64.strict_encode64(your_content_here)
NOTE: in this case strict_encode64 is preferrable over just encode64 because it doesn't add any newlines. (credit goes to Sergey Mell for pointing that out)
From docs:
encode64 - ... Line feeds are added to every 60 encoded characters.
strict_encode64 - ... No line feeds are added.
Store it in database (optionaly)
I suggest you to create a separate ActiveRecord model for that, with field of type text to keep base64 representation of image.
Display it in views
You can provide data-url to src attribute of img tag, so, the browser will decode image from base64 and display it just like regular image:
<img src="data:image/png;base64,YOUR_BASE64_HERE"/>
Don't forget to change image format to whatever format you are using in data:image/png section.
UPDATE (2018-08-22): I have tried to use urlsafe_encode64, as suggested by Xornand, and for me it produces the output that is not recognized as image by the browser.
Tried in both Firefox 61.0.2 and Chromium 68.0.3440.106.
For the sake of reference and to enable experimentation, here are results themselves.
Image used as "original" (resized it to be even more small to reduce the size of base64 output):
encode64:
/9j/4AAQSkZJRgABAQEAYABhAAD/4QBARXhpZgAASUkqAAgAAAABAGmHBAAB
AAAAGgAAAAAAAAACAAKgCQABAAAAZAAAAAOgCQABAAAAfwAAAAAAAAD/2wBD
AAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwc
KDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIy
MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/
/gA0T3B0aW1pemVkIGJ5IEpQRUdtaW5pIDMuMTQuMTQuNzI2NzA4NjAgMHhm
ZjIzZjM3OQD/wAARCAB/AGQDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAA
AAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIh
MUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3
ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKT
lJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi
4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQF
BgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMi
MoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZH
SElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJma
oqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq
8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDyNj85+tBbFEvE7j0Y/wA6YxrA2HIx
Jqwp4rS0LwvfayyspWKFj99z1+grvrf4TCSEMLqVzjqu3+VTKSRUYtnmQrS0
WTy9Tib0DfyNdNq/wz1WwUvbAyp6MMH/AArl7eKaw1JUuImjdcgq4x2NS2mt
CopqSuZkLf6Vdn/ZevR/ADgaHAv/AE9E/qteaQn9/de6NXoHw/c/YLVSetye
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
opvn2CRxLDL5ShB8ihgB+GKKBH//2Q==
strict_encode64:
/9j/4AAQSkZJRgABAQEAYABhAAD/4QBARXhpZgAASUkqAAgAAAABAGmHBAABAAAAGgAAAAAAAAACAAKgCQABAAAAZAAAAAOgCQABAAAAfwAAAAAAAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL//gA0T3B0aW1pemVkIGJ5IEpQRUdtaW5pIDMuMTQuMTQuNzI2NzA4NjAgMHhmZjIzZjM3OQD/wAARCAB/AGQDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDyNj85+tBbFEvE7j0Y/wA6YxrA2HIxJqwp4rS0LwvfayyspWKFj99z1+grvrf4TCSEMLqVzjqu3+VTKSRUYtnmQrS0WTy9Tib0DfyNdNq/wz1WwUvbAyp6MMH/AArl7eKaw1JUuImjdcgq4x2NS2mtCopqSuZkLf6Vdn/ZevR/ADgaHAv/AE9E/qteaQn9/de6NXoHw/c/YLVSetyePxFKrsXT/wAzoPFigaovH/LIfzNYHet/xW2dVHtGP61gfxVqcKLMNXojjFUIzVyI0AaSN8tFRKw2iigDxi9+S8mX0c1b0mxa6nDNGXUnCr/eNPNgbu5aduIzg4HU1oaZdvp16JBErIo2hCe1M3SO20uKGwgVZdW0+G9Yjy4XfcR7EjgfSuv8M6pqc91LZzWitLD1aORQCOxGOoI9K8t1W90vU9k0cM1vMgySuOvpVrwvrM1lqguXnYkptLE9QOgqJQW5tGfQ92Sa4+YXMIEZ/vNkiud8Q+GdM1C4U3EAKn7rrwy/jV3Tdfi1G1yrBiv3sH2qw7xOgjBJ5PfNc8tNUax8zx7xb8OJtAt5tU093uLIofMB+9HnufUe9R+BDstrTP8Az8/4V7KGla3aEhJIWUgqwzkHqD7Vxh0zStHuGitrRYvn3hdxIBP16UOfMrMpQszO8TtnVT7ItYWfmrvP7PttQgDSJGc8cZyKzbvwaWQvZTZcfwMev41pGtF7nJLDSitNTnY2xVqFqptFLbzNDMjJIhwVYYIqaNua2Odmkr8UVCrfLRQI8fkuZAkTq7KSuCQfSrumTmdzE7Ev1BJ5PtVc2h8pR6E01LaRHDoSGByCO1O6Ntbm6I8KSfujnJNLp5Ny2YSMZPzH/CobK9mdTBOvDcbgOPxrS0q3FrMzMg2hSMdATSRdzodD1M6ULhgcuy8Z9feuz8K6m7Rs8nzDOcV5vZ2sjYeT+Lk5712emSER4XACjtUTjc0gzupbw+SZVXI6kKOgrh9Vkj1LV5o7dlMipn3Fbdtq4is5uN+xGZlxyR3rza21Rv7QkugzbgeOe1Z8hpzndaTPIlqI5wWKjhgBW7pV1NNOqgDHGD2+hFcPp+tQFCJxt3Hsa6fTr2OJCImKc8NwSKiUS1I1fFehW+sWMl1arjULZMsg6yIO2PUdq82j4Nes6JdFplaQDzF4LDoR/hXA+KdM/szxDcIi4hlPmx49D2/A5Fa05aWZx14Wd0ZyniimA8UVqYHIf2YxUAKT9BT10iU/8smrtFt+igKDj0xUgszj+EAjpnvWWp1XRx8eiTHny8fU1ZXSJY0JbBXqQCa61bMocMB+dP8AsIOFwSPXrRewHPQSIqtnAA/StbSbyJWdJABjpmsnVITp95sK5VxxVCZ7hIfNAIReorQSN241rZp14DgE5xjqPpXG2MFzOGkWKVgxyQqkk0GZ76QDkAnkDvWnYtJB8ylyxGAFOD/Opehe4W1o8TgzFkXI3BgRj2Nd3oFpaRyrKLndkgDL8EHsa4GUzzXn7wSIrDOWFamlXX2Z1UuXj6Z6YOf8DWctTRHpVsklndgRO5CneoI6gfeXjrxn8q534mzXE8lk9kkxePdv8sEgA4xnH0NaFnrCSTRqXDsjI69iCDj/AB/OrmmXMRu78yuMRuEXP4nH6ipS1CTSVzyIavfLwWGR6rRXsk2q6akhDFWPrRV6kc0exzYt125bJ5x171ZjRMBgBn37f54pflI+RgWxywPQn61Irp8rE9T9AB/XtTuZFTUL2PT41eU5JbCr/WqTa7HKh+zwOHI6ucD9OtXdW0watbgCURyq2UbHHTvzWNB4ZvlYia6iSNc5cAnFVFxtqGpl3yfanNxPK3m5zurIvbtlQxlgQRxiu1j0S0Q4Y/aD3yeled6skkGry26cDzMADsKaak9B7Ghp8IRN38Zq4kRwcdR2FV7dvL24BNaMO1U8xjggfnQ0NMdFH5u1pZgoTkD+lSGzDMyxyDAP3QOTVAk4AUkY5GBUwvXRspuLduO/+FQ0aJmipW3uLeRWAYdQO/HWqUurEu7hyGdiSB64qPVLosgdk2yDAB/z7YrC8w4OR9DTSIm+hpSamxcnOfq1FZhznjB+uKKqxlc7mbW4wxUEB3PY9PWs648RyqC0UmRjGCDz9M1z7Ssw6E+h/wDr0EyDCuyKTwBn/wCtT5A5i/J4mvy52yMoI5Kt/wDrqnJ4jvSmDO6jOcfhVSQBztMnA7VnzWzgMVOfenyhcsy6nqFyCyzOV6fexVBp5klRnVt3c9ah8+WDCyLj+tWYbxfNUkgnPemlYNzctb1EjVpGwBST6ukjbUbC9u1Vbm5glRfkAJ61HDFaMwWWIbCeqnBFLQpXNGOZnXhjnHQ1t2WlyahbkrKqMv8AfbFQWekaWsSMNQuMH+BcEj8xXQ28XhuBQLiW6fI5Es23H4Lis5M1Rzl/avCfIlGHBznOQfpWFMJY3MYHOevtXWavf6NLtis4eQNibXJOe3Wsz7EJYVJ+WTJ4Y8/lTiRM5vzH7LkfQ0VsGylPKqCP9oYNFWZ2LqaRMedwQcA5GBnPT+dTx+G55QjuoUYJyT64/wDr126m1FuXWDdEyB2O3qehz9MdqmSKBAxODGg4DKRkk5+o/Gq5hWOCl8MOoARCcn7w7+1V5fDtwgYlDkLnaRyRz/8AW/OvRzCm5I5FxtIXDAnaep5/EYqZbeP7SGLFcIV5XIwecE9e4HXHFJTHynlf/CO3kilhbrIh74yaiTwZNMob7MQu4/cznOcYwP5V69BaLIXO5d5GMrk4JPcdccgjJ4H51KsEciufOWNVj+Vwo477h+J7envRzhynkcHw91K4d/LMsSocev6fiK04fhlqu8AahApyAQ69M9Mnp+HuK9KmDKy+RC+TglyflHJwQfX6/wD16mgikmMZSaJdoJYkgsWHGMgf/XqHJlKKPOovhnqzXBR9aiUA4yik8YznH4VswfCiwkPmXWq3kxUElUCqT6DGPr+Vd5GrWrKIwXUqxycFwccf1/Sk3MUiXa6MxCsfKJUYIHfvz19vyV2By8XgvTLBFitLWJSyk72OW47kfhWTc6TdW7yqU/dISCSpPy8dwTnHNegvGoAizH5oAJBXIkx6jt25z/KmSxGUqhQLGwJYseoBHyj06nii4Hmi2CwqEMg+uDz+VFehrpNm67sbc9uf6Yopgc2LlYrUSRmQxbifkB6AjBGMce3epLaJkYzfvGMq/IrEkD2OO+fTPaq9vP5ixIYkMkyEyehVeP6irULLGsZLO4UGRUXAbAA69jyfXvVEk6P587xzLtTftUq3PbB9c85okCLbkvviQkBGwxb1HqfQ/nxUT2zzxqY3O+QCRMSbQo/75PNKpULDtDP5rELliAcj3z2J/OkMekUtvK0cYRkEahGVdmck9cn0AOetSLd7IxNK25WRdqqBg5ByTnjoCTUkJkkgl+0IglRsAZ47ZGR6f0qAXm+KGJCFIUAEOxYDjuVPP40ASIjGTz4RDJLLj5mUAEj7v69xU1tciV189CSSzHBGBjscY9/z7GkDTm5t5ZZFbBMYC9/XnjuKhUyyWsaxIsJRmXaVVgMkjHoec+nA96Vh3L3kjc0gmQdAXTGRjv3HrxxxmnXELXksZlOYo1Mu5xtKk579CMDseuKy7Uu1vb/ZFZImZm25AJHX6etWiyNdC3mQqzyDYoY4Kjk9OnBzz60hlkyxzweXMpmGMBk3bDkAgkg9MVdF5Daw5TAUL/q26qOg69ecDnFUCUhLkRvJwzZUj5SBgcHHBANKpeUCcp5kY3AggHIHPTgYoAtOZWCGWxt7klQVkZl5Htx0opvn2CRxLDL5ShB8ihgB+GKKBH//2Q==
urlsafe_encode64:
_9j_4AAQSkZJRgABAQEAYABhAAD_4QBARXhpZgAASUkqAAgAAAABAGmHBAABAAAAGgAAAAAAAAACAAKgCQABAAAAZAAAAAOgCQABAAAAfwAAAAAAAAD_2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL_2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL__gA0T3B0aW1pemVkIGJ5IEpQRUdtaW5pIDMuMTQuMTQuNzI2NzA4NjAgMHhmZjIzZjM3OQD_wAARCAB_AGQDASIAAhEBAxEB_8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL_8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4-Tl5ufo6erx8vP09fb3-Pn6_8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL_8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3-Pn6_9oADAMBAAIRAxEAPwDyNj85-tBbFEvE7j0Y_wA6YxrA2HIxJqwp4rS0LwvfayyspWKFj99z1-grvrf4TCSEMLqVzjqu3-VTKSRUYtnmQrS0WTy9Tib0DfyNdNq_wz1WwUvbAyp6MMH_AArl7eKaw1JUuImjdcgq4x2NS2mtCopqSuZkLf6Vdn_ZevR_ADgaHAv_AE9E_qteaQn9_de6NXoHw_c_YLVSetyePxFKrsXT_wAzoPFigaovH_LIfzNYHet_xW2dVHtGP61gfxVqcKLMNXojjFUIzVyI0AaSN8tFRKw2iigDxi9-S8mX0c1b0mxa6nDNGXUnCr_eNPNgbu5aduIzg4HU1oaZdvp16JBErIo2hCe1M3SO20uKGwgVZdW0-G9Yjy4XfcR7EjgfSuv8M6pqc91LZzWitLD1aORQCOxGOoI9K8t1W90vU9k0cM1vMgySuOvpVrwvrM1lqguXnYkptLE9QOgqJQW5tGfQ92Sa4-YXMIEZ_vNkiud8Q-GdM1C4U3EAKn7rrwy_jV3Tdfi1G1yrBiv3sH2qw7xOgjBJ5PfNc8tNUax8zx7xb8OJtAt5tU093uLIofMB-9HnufUe9R-BDstrTP8Az8_4V7KGla3aEhJIWUgqwzkHqD7Vxh0zStHuGitrRYvn3hdxIBP16UOfMrMpQszO8TtnVT7ItYWfmrvP7PttQgDSJGc8cZyKzbvwaWQvZTZcfwMev41pGtF7nJLDSitNTnY2xVqFqptFLbzNDMjJIhwVYYIqaNua2Odmkr8UVCrfLRQI8fkuZAkTq7KSuCQfSrumTmdzE7Ev1BJ5PtVc2h8pR6E01LaRHDoSGByCO1O6Ntbm6I8KSfujnJNLp5Ny2YSMZPzH_CobK9mdTBOvDcbgOPxrS0q3FrMzMg2hSMdATSRdzodD1M6ULhgcuy8Z9feuz8K6m7Rs8nzDOcV5vZ2sjYeT-Lk5712emSER4XACjtUTjc0gzupbw-SZVXI6kKOgrh9Vkj1LV5o7dlMipn3Fbdtq4is5uN-xGZlxyR3rza21Rv7QkugzbgeOe1Z8hpzndaTPIlqI5wWKjhgBW7pV1NNOqgDHGD2-hFcPp-tQFCJxt3Hsa6fTr2OJCImKc8NwSKiUS1I1fFehW-sWMl1arjULZMsg6yIO2PUdq82j4Nes6JdFplaQDzF4LDoR_hXA-KdM_szxDcIi4hlPmx49D2_A5Fa05aWZx14Wd0ZyniimA8UVqYHIf2YxUAKT9BT10iU_8smrtFt-igKDj0xUgszj-EAjpnvWWp1XRx8eiTHny8fU1ZXSJY0JbBXqQCa61bMocMB-dP8AsIOFwSPXrRewHPQSIqtnAA_StbSbyJWdJABjpmsnVITp95sK5VxxVCZ7hIfNAIReorQSN241rZp14DgE5xjqPpXG2MFzOGkWKVgxyQqkk0GZ76QDkAnkDvWnYtJB8ylyxGAFOD_Opehe4W1o8TgzFkXI3BgRj2Nd3oFpaRyrKLndkgDL8EHsa4GUzzXn7wSIrDOWFamlXX2Z1UuXj6Z6YOf8DWctTRHpVsklndgRO5CneoI6gfeXjrxn8q534mzXE8lk9kkxePdv8sEgA4xnH0NaFnrCSTRqXDsjI69iCDj_AB_OrmmXMRu78yuMRuEXP4nH6ipS1CTSVzyIavfLwWGR6rRXsk2q6akhDFWPrRV6kc0exzYt125bJ5x171ZjRMBgBn37f54pflI-RgWxywPQn61Irp8rE9T9AB_XtTuZFTUL2PT41eU5JbCr_WqTa7HKh-zwOHI6ucD9OtXdW0watbgCURyq2UbHHTvzWNB4ZvlYia6iSNc5cAnFVFxtqGpl3yfanNxPK3m5zurIvbtlQxlgQRxiu1j0S0Q4Y_aD3yeled6skkGry26cDzMADsKaak9B7Ghp8IRN38Zq4kRwcdR2FV7dvL24BNaMO1U8xjggfnQ0NMdFH5u1pZgoTkD-lSGzDMyxyDAP3QOTVAk4AUkY5GBUwvXRspuLduO_-FQ0aJmipW3uLeRWAYdQO_HWqUurEu7hyGdiSB64qPVLosgdk2yDAB_z7YrC8w4OR9DTSIm-hpSamxcnOfq1FZhznjB-uKKqxlc7mbW4wxUEB3PY9PWs648RyqC0UmRjGCDz9M1z7Ssw6E-h_wDr0EyDCuyKTwBn_wCtT5A5i_J4mvy52yMoI5Kt_wDrqnJ4jvSmDO6jOcfhVSQBztMnA7VnzWzgMVOfenyhcsy6nqFyCyzOV6fexVBp5klRnVt3c9ah8-WDCyLj-tWYbxfNUkgnPemlYNzctb1EjVpGwBST6ukjbUbC9u1Vbm5glRfkAJ61HDFaMwWWIbCeqnBFLQpXNGOZnXhjnHQ1t2WlyahbkrKqMv8AfbFQWekaWsSMNQuMH-BcEj8xXQ28XhuBQLiW6fI5Es23H4Lis5M1Rzl_avCfIlGHBznOQfpWFMJY3MYHOevtXWavf6NLtis4eQNibXJOe3Wsz7EJYVJ-WTJ4Y8_lTiRM5vzH7LkfQ0VsGylPKqCP9oYNFWZ2LqaRMedwQcA5GBnPT-dTx-G55QjuoUYJyT64_wDr126m1FuXWDdEyB2O3qehz9MdqmSKBAxODGg4DKRkk5-o_Gq5hWOCl8MOoARCcn7w7-1V5fDtwgYlDkLnaRyRz_8AW_OvRzCm5I5FxtIXDAnaep5_EYqZbeP7SGLFcIV5XIwecE9e4HXHFJTHynlf_CO3kilhbrIh74yaiTwZNMob7MQu4_cznOcYwP5V69BaLIXO5d5GMrk4JPcdccgjJ4H51KsEciufOWNVj-Vwo477h-J7envRzhynkcHw91K4d_LMsSocev6fiK04fhlqu8AahApyAQ69M9Mnp-HuK9KmDKy-RC-TglyflHJwQfX6_wD16mgikmMZSaJdoJYkgsWHGMgf_XqHJlKKPOovhnqzXBR9aiUA4yik8YznH4VswfCiwkPmXWq3kxUElUCqT6DGPr-Vd5GrWrKIwXUqxycFwccf1_Sk3MUiXa6MxCsfKJUYIHfvz19vyV2By8XgvTLBFitLWJSyk72OW47kfhWTc6TdW7yqU_dISCSpPy8dwTnHNegvGoAizH5oAJBXIkx6jt25z_KmSxGUqhQLGwJYseoBHyj06nii4Hmi2CwqEMg-uDz-VFehrpNm67sbc9uf6Yopgc2LlYrUSRmQxbifkB6AjBGMce3epLaJkYzfvGMq_IrEkD2OO-fTPaq9vP5ixIYkMkyEyehVeP6irULLGsZLO4UGRUXAbAA69jyfXvVEk6P587xzLtTftUq3PbB9c85okCLbkvviQkBGwxb1HqfQ_nxUT2zzxqY3O-QCRMSbQo_75PNKpULDtDP5rELliAcj3z2J_OkMekUtvK0cYRkEahGVdmck9cn0AOetSLd7IxNK25WRdqqBg5ByTnjoCTUkJkkgl-0IglRsAZ47ZGR6f0qAXm-KGJCFIUAEOxYDjuVPP40ASIjGTz4RDJLLj5mUAEj7v69xU1tciV189CSSzHBGBjscY9_z7GkDTm5t5ZZFbBMYC9_XnjuKhUyyWsaxIsJRmXaVVgMkjHoec-nA96Vh3L3kjc0gmQdAXTGRjv3HrxxxmnXELXksZlOYo1Mu5xtKk579CMDseuKy7Uu1vb_ZFZImZm25AJHX6etWiyNdC3mQqzyDYoY4Kjk9OnBzz60hlkyxzweXMpmGMBk3bDkAgkg9MVdF5Daw5TAUL_q26qOg69ecDnFUCUhLkRvJwzZUj5SBgcHHBANKpeUCcp5kY3AggHIHPTgYoAtOZWCGWxt7klQVkZl5Htx0opvn2CRxLDL5ShB8ihgB-GKKBH__2Q==

Resources