68

I want to base-64 encode a PNG file, to include it in a data:url in my stylesheet. How can I do that?

I’m on a Mac, so something on the Unix command line would work great. A Python-based solution would also be grand.

Paul D. Waite
  • 96,640
  • 56
  • 199
  • 270

7 Answers7

93

This should do it in Python:

import base64

binary_fc       = open(filepath, 'rb').read()  # fc aka file_content
base64_utf8_str = base64.b64encode(binary_fc).decode('utf-8')

ext     = filepath.split('.')[-1]
dataurl = f'data:image/{ext};base64,{base64_utf8_str}'

Thanks to @cnst comment, we need the prefix data:image/{ext};base64,

Thanks to @ramazanpolat answer, we need the decode('utf-8')

Nam G VU
  • 33,193
  • 69
  • 233
  • 372
Jon
  • 428,835
  • 81
  • 738
  • 806
  • That looks good. When I try to use the results in my CSS file, Firefox is currently telling me that the image is corrupt or truncated, but I may be doing something wrong somewhere. – Paul D. Waite Jun 16 '11 at 17:32
  • @Paul D. Waite: What does the Base64 output look like, and how do you use it in your CSS? – BoltClock Jun 16 '11 at 18:09
  • 1
    @BoltClock: the output I got was `iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAP0lEQVQY02P4/fv3f1z4ypUrcDYD\niIOMkQEyH6tCdDEQZkDWRZKJ6CajKEQ3BV0Mr4noGhiw6SbJjVhNJEYhAKztct58fLlaAAAAAElF\nTkSuQmCC\n`. In my CSS file, it looks like `background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAP0lEQVQY02P4/fv3f1z4ypUrcDYD\niIOMkQEyH6tCdDEQZkDWRZKJ6CajKEQ3BV0Mr4noGhiw6SbJjVhNJEYhAKztct58fLlaAAAAAElF\nTkSuQmCC\n);`. – Paul D. Waite Jun 16 '11 at 18:43
  • 3
    @PaulD.Waite: Those `\n`s definitely look out of place in there. – Jon Jun 16 '11 at 19:23
  • @Jon: aha! Right: those are newline characters that Python's printing. Turns out I needed to url-encode them so they'd work in the CSS file. I'll amend the answer accordingly. – Paul D. Waite Jun 17 '11 at 09:23
  • @Miles: ah, are they not required then? – Paul D. Waite Jun 23 '11 at 16:47
  • 13
    This works, but the resulting string should additionally be prepended with `"data:image/png;base64,"` -- the code above alone doesn't do that. – cnst Sep 30 '13 at 18:12
  • How do you do this in python3 – qed Apr 25 '14 at 08:08
  • I tried the original version of this answer that did `.encode("base64")` and it seemed to work but the output was bigger than I thought. I investigated and noticed that it was putting unnecessary newline characters in the output. Also, the `urllib.quote` call was URL-encoding the newlines, which was making them even bigger. I fixed this answer so it does not add unnecessary newlines. – Elias Zamaria Aug 13 '15 at 20:12
57

In python3, base64.b64encode returns a bytes instance, so it's necessary to call decode to get a str, if you are working with unicode text.

# Image data from [Wikipedia][1]
>>>image_data = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x05\x00\x00\x00\x05\x08\x06\x00\x00\x00\x8do&\xe5\x00\x00\x00\x1cIDAT\x08\xd7c\xf8\xff\xff?\xc3\x7f\x06 \x05\xc3 \x12\x84\xd01\xf1\x82X\xcd\x04\x00\x0e\xf55\xcb\xd1\x8e\x0e\x1f\x00\x00\x00\x00IEND\xaeB`\x82'

# String representation of bytes object includes leading "b" and quotes,  
# making the uri invalid.
>>> encoded = base64.b64encode(image_data) # Creates a bytes object
>>> 'data:image/png;base64,{}'.format(encoded)
"data:image/png;base64,b'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='"


# Calling .decode() gets us the right representation
>>> encoded = base64.b64encode(image_data).decode('ascii')
>>> 'data:image/png;base64,{}'.format(encoded)
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='

If you are working with bytes directly, you can use the output of base64.b64encode without further decoding.

>>> encoded = base64.b64encode(image_data)
>>> b'data:image/png;base64,' + encoded
b'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='
snakecharmerb
  • 47,570
  • 11
  • 100
  • 153
21
import base64

def image_to_data_url(filename):
    ext = filename.split('.')[-1]
    prefix = f'data:image/{ext};base64,'
    with open(filename, 'rb') as f:
        img = f.read()
    return prefix + base64.b64encode(img).decode('utf-8')
ramazan polat
  • 7,111
  • 1
  • 48
  • 76
4

This should do it in Unix:

b64encode filename.png X | sed '1d;$d' | tr -d '\n' > b64encoded.png

The encoded image produced by b64encode includes a header and footer and no line longer than 76 characters. This format is typical in SMTP communications.

To make the encoded image embeddable in HTML/CSS, the sed and tr commands remove the header/footer (first & last lines) and all newlines, respectively.

Then just simply use the long encoded string in HTML

<img src="data:image/png;base64,ENCODED_PNG">

or in CSS

url(data:image/png;base64,ENCODED_PNG)
Clint Pachl
  • 10,848
  • 6
  • 41
  • 42
  • 1
    In some distros there's no `b64encode` included. In that case, you'd want to use `uuencode -m`, which is a synonym. – matth Feb 03 '16 at 07:24
2

b64encode is not installed by default in some distros (@Clint Pachl's answer), but python is.

So, just use:

python -mbase64 image.jpeg | tr -d '\n' > b64encoded.txt

In order to get base64 encoded image from the command line.

The remaining steps were already answered by @Clint Pachl (https://stackoverflow.com/a/20467682/1522342)

iuridiniz
  • 2,213
  • 24
  • 26
1

This should work in Python3:

from io import BytesIO
import requests, base64

def encode_image(image_url):
    buffered = BytesIO(requests.get(image_url).content)
    image_base64 = base64.b64encode(buffered.getvalue())
    return b'data:image/png;base64,'+image_base64

Call decode to get str as in python3 base64.b64encode returns a bytes instance.

mlbishnoi
  • 457
  • 5
  • 13
  • Why did you use bytesio? You can just use directly like image_base64 = base64.b64encode(requests.get(image_url).content) – Ajay Apr 28 '23 at 07:33
0

And just for the record, if you want to do it in Node.js instead:

const fs = require('fs');
const base64encodedString = fs.readFileSync('image_file.jpg', {encoding:'base64'});
Paul D. Waite
  • 96,640
  • 56
  • 199
  • 270