Replacing all images in a CSS file with base64 encoded strings from the command line
Basically I'm trying to write a clever single line of commands that outputs base64 encoded strings in place of where there used to be a path to an image. So:
background-image: url(path/to/my/image.png);
…would turn into:
background-image: url(data:image/png;base64,ABvR0…+tC==);
I normally turn an image into its base64 encoded string via:
openssl enc -base64 -in path/to/my/image.png
Which outputs the base64… but with newlines in it. This is fixed by piping it through tr
like:
openssl enc -base64 -in path/to/my/image.png | tr -d '\n'
which just outputs a long base64 encoded string. By using pbcopy (on Mac OS) that gets sent to the clipboard, like so:
openssl enc -base64 -in path/to/my/image.png | tr -d '\n' | pbcopy
Very nice for ad hoc replacing an occasional image with its base64 representation but what I'd like to do is have this automated for replacing all occurrences of 开发者_JS百科url(path/to/whatever.ext)
in a file by their respective base64 strings. Making sure there are only actual paths and no data-uris in there is out of scope here :)
I have been trying to replace stuff with sed
but I got stuck in its horrible documentation. It's not too difficult to find the occurences of the url(…)
pattern in a css file, but the bit between parenthesis needs to be replaced by the output of the above command and I am clueless if this is at all possible. So, there it is, help or some pointers (do I need to look into awk as well?) would be much appreciated! A 'you cannot do this without a proper script' would also do of course :)
With openssl
tool
#!/bin/bash
awk -F'[()]' -v q="'" '
/background-image: url(.*)/ {
cmd=sprintf("openssl enc -base64 -in %s | tr -d %c\\n%c",$2,q,q)
cmd | getline b64
close(cmd)
$0=$1 "(data:image/png;base64," b64 ");"
}1' /path/to/css/file
Proof of Concept
See HERE for a working example
With base64
tool
#!/bin/bash
awk -F'[()]' '
/background-image: url(.*)/ {
cmd=sprintf("base64 -w0 %s",$2)
cmd | getline b64
close(cmd)
$0=$1 "(data:image/png;base64," b64 ");"
}1' /path/to/css/file
Proof of Concept
See HERE for a working example
Consider using Perl rather than sed. Skip awk altogether, its awkward :-). (Actually it was one of the first programming languages I learned, but Perl is much better).
Better still, don't do that at all as it won't work in 50% of users' browsers. At any rate, be very, very sure you don't care about those users, before you tell them so in this way.
I don't have a lot of experience with ccs and png files, but given your very helpful I have this string, I want that string descriptions, Here's a first draft
#! /bin/ksh # -vx (bash should work too)
usageMsg="pngReplacer pngFile ccsFile"
case $# in
[!2] ) print "usage: ${usageMsg}" ; exit 1
esac
set -- ${@} # I'm surprised that we need this line
base64Str=$(openssl enc -base64 -in "$1" 2>/dev/null | tr -d '\n')
{
if ${testMode:-false} ; then
print -- " background-image: url(path/to/my/image.png);"
else
cat "$2"
fi
} | sed 's@url(.*)@url(data:image/png;base64,'"$base64Str"')@g'
Lots of fragility here
- do I have this right, png file and css file, right?
- you know about ksh/bash $1 $2 etc arguments, right?
- the test for argument count is ok, but
- you'd probably really want to confirm that $1 and $2 really exist
- no opportunity to specify an outputFile, run it as
pngReplacer pngFile ccsFile > newCcsFile
- if the stuff in { } gives you the heebie-geebies, you could cut it all out, right to the '|' char just before the sed, and put the $2 at the end of the sed command.
- I would be surprised if AIX sed could handle a 280 char replacement expression, but I don't have it handy to test with anymore. In general, seds are fragile about things like that.
- biggest fragility of all, the @reg-exp@replace-exp@ delimiter (@). If you wind up with an @ sign in the base64Str, then you'll have to find a char to use that is not in the new base64Str.
I hope this helps.
This command line I wrote encodes all images contained into an HTML file instead.
It can easily be adapted to encode all images in a CSS file (by editing the base64images function).
It just requires the PHP command line interpreter.
#!/usr/bin/php
<?php
/**
* This command line tool encodes all images contained in the specified HTML file.
* The encoded contents will be returned on standard output.
* Use redirection to store it into a file.
* @author Massimiliano Alessandri <m.alessandri@spaziocolore.it>
*/
/**
* Converts a "src" property embedding the referenced image.
* This way, the recipient won't have to click on "display images" to view the images.
* @param array $matches the array sent by the preg_replace_callback function
* @return string the encoded "src" property
*/
function base64images($matches) {
return 'src="data:'.mime_content_type($matches[1]).';base64,'.base64_encode(file_get_contents($matches[1])).'"';
}
if($argc < 2) {
echo 'Usage: '.$argv[0].' [file to be encoded]';
echo 'The encoded file will be returned to standard output.';
exit;
}
$filename = $argv[1];
if(file_exists($filename))
echo preg_replace_callback('/src="([^"]+)"/', 'base64images', file_get_contents($filename));
else
echo 'File not found: '.$filename."\n";
精彩评论