开发者

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";
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜