Skip to content

Commit 19f722a

Browse files
committed
Add ImageDataUri class to convert image uri to file
1 parent fe2f603 commit 19f722a

File tree

2 files changed

+120
-0
lines changed

2 files changed

+120
-0
lines changed

lib/image_data_uri.rb

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
require "base64"
2+
require "pathname"
3+
4+
class ImageDataUri
5+
attr_reader :content_type, :encoding, :data
6+
7+
def initialize(data_uri)
8+
parse_data_uri(data_uri)
9+
end
10+
11+
def to_file(path)
12+
path_with_extension = ensure_file_extension(path)
13+
File.binwrite(path_with_extension, to_file_data)
14+
path_with_extension
15+
end
16+
17+
def to_file_data
18+
Base64.decode64(@data)
19+
end
20+
21+
private
22+
23+
def parse_data_uri(data_uri)
24+
raise ArgumentError, "Invalid data URI format" unless data_uri.start_with?("data:")
25+
26+
parts = data_uri.split(",", 2)
27+
raise ArgumentError, "Invalid data URI format" if parts.length != 2
28+
29+
metadata, @data = parts
30+
metadata_parts = metadata.split(";")
31+
32+
@content_type = metadata_parts[0].split(":")[1]
33+
@encoding = metadata_parts[1] if metadata_parts.length > 1
34+
35+
raise ArgumentError, "Unsupported encoding" if @encoding && @encoding != "base64"
36+
end
37+
38+
def ensure_file_extension(path)
39+
pathname = Pathname.new(path)
40+
return path if pathname.extname.size > 1
41+
42+
extension = mime_to_extension(@content_type)
43+
"#{path}#{extension}"
44+
end
45+
46+
def mime_to_extension(mime_type)
47+
{
48+
"image/jpeg" => ".jpg",
49+
"image/png" => ".png",
50+
"image/gif" => ".gif",
51+
"image/webp" => ".webp",
52+
"image/svg+xml" => ".svg"
53+
}.fetch(mime_type, ".bin")
54+
end
55+
end

spec/lib/image_data_uri_spec.rb

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
require "spec_helper"
2+
require_relative "../../lib/image_data_uri"
3+
4+
RSpec.describe ImageDataUri do
5+
let(:png_data_uri) { "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==" }
6+
let(:jpeg_data_uri) { "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAABAAEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iiigD//2Q==" }
7+
8+
describe "#initialize" do
9+
it "parses a valid PNG data URI" do
10+
image_uri = ImageDataUri.new(png_data_uri)
11+
expect(image_uri.content_type).to eq("image/png")
12+
expect(image_uri.encoding).to eq("base64")
13+
end
14+
15+
it "parses a valid JPEG data URI" do
16+
image_uri = ImageDataUri.new(jpeg_data_uri)
17+
expect(image_uri.content_type).to eq("image/jpeg")
18+
expect(image_uri.encoding).to eq("base64")
19+
end
20+
21+
it "raises an error for invalid data URI format" do
22+
expect { ImageDataUri.new("invalid_uri") }.to raise_error(ArgumentError, "Invalid data URI format")
23+
end
24+
25+
it "raises an error for unsupported encoding" do
26+
expect { ImageDataUri.new("data:image/png;base32,ABCDEF") }.to raise_error(ArgumentError, "Unsupported encoding")
27+
end
28+
end
29+
30+
describe "#to_file" do
31+
let(:image_uri) { ImageDataUri.new(png_data_uri) }
32+
33+
before do
34+
allow(File).to receive(:binwrite)
35+
end
36+
37+
it "writes the file data to the specified path" do
38+
path = "test_image"
39+
expect(File).to receive(:binwrite).with("#{path}.png", kind_of(String))
40+
image_uri.to_file(path)
41+
end
42+
43+
it "appends the correct extension when not provided" do
44+
expect(image_uri.to_file("image")).to eq("image.png")
45+
end
46+
47+
it "does not modify the path when extension is provided" do
48+
expect(image_uri.to_file("image.png")).to eq("image.png")
49+
end
50+
51+
it "uses .bin extension for unknown mime types" do
52+
unknown_data_uri = "data:image/unknown;base64,ABCDEF"
53+
image_uri = ImageDataUri.new(unknown_data_uri)
54+
expect(image_uri.to_file("image")).to eq("image.bin")
55+
end
56+
end
57+
58+
describe "#to_file_data" do
59+
it "returns decoded file data" do
60+
image_uri = ImageDataURI.new(png_data_uri)
61+
expect(image_uri.to_file_data).to be_a(String)
62+
expect(image_uri.to_file_data.bytesize).to be > 0
63+
end
64+
end
65+
end

0 commit comments

Comments
 (0)