用命令行将CSS文件中的所有图像替换为base64编码字符串

7
基本上我正在尝试编写一行聪明的命令,将base64编码的字符串输出到原来是图像路径的位置。所以:
background-image: url(path/to/my/image.png);

…将转变为:

background-image: url(data:image/png;base64,ABvR0…+tC==);

通常我通过以下方式将图像转换为其Base64编码字符串:

openssl enc -base64 -in path/to/my/image.png

这会输出带有换行符的base64编码。可以通过使用tr来修复此问题,例如:

openssl enc -base64 -in path/to/my/image.png | tr -d '\n'

这段代码仅输出一个长的Base64编码字符串。通过使用Mac OS上的pbcopy,将其发送到剪贴板中,如下所示:

openssl enc -base64 -in path/to/my/image.png | tr -d '\n' | pbcopy

非常适合临时替换图像为其base64表示,但我想自动替换文件中所有出现的url(path/to/whatever.ext)为它们各自的base64字符串。确保其中只有实际路径而没有data-uri超出了本文的范围:)
我一直在尝试使用sed替换内容,但我卡在了可怕的文档上。在CSS文件中找到url(…)模式的出现并不太困难,但是括号之间的部分需要替换为上述命令的输出,如果这可能的话,我就无从下手了。所以,这就是问题所在,希望能得到帮助或一些指导(我是否还需要研究awk?)。当然,如果说“你不能没有一个适当的脚本做到这一点”的话也可以:)

2
除非你想让50%的用户在使用IE 6、7、8浏览器时无法查看图片,否则不要这样做。建议几年后再尝试。 - Ben
1
通常在为某些移动浏览器开发时,会这样做以减少HTTP开销,因此IE 6不是问题。 - Liza Daly
@Ben 已经注意到了。然而,我很幸运不必关心IE6。对于IE7,有一个mhtml解决方案,并且在IE8中它完美地工作。 - Matijs
2
请查看 Juicer - Mathias Bynens
4个回答

9

使用openssl工具

#!/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

概念证明

请参见此处,有一个工作示例。


使用base64 工具

#!/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

概念验证

请点击这里查看一个可行的示例。


这看起来有些有前途,但我没有base64工具(但我想我可以轻松地编写一个使用openssltr的脚本)。但是,我无法让它工作。虽然我不是100%确定,但根据经验我知道,并非所有的Mac OS X unix命令都与它们的Linux版本完全相同。 - Matijs
1
@Matijs 看看我的更新答案。你没能让它工作的原因很可能是因为你必须使用一个变量来表示 tr '\n' 周围的单引号。如果你试图使用一个字面上的单引号,它会干扰到整个 awk 脚本周围的单引号,这些单引号是为了防止 shell 解释任何 awk 字符而存在的。 - SiegeX
@Matijs 当你说“打开太多文件”时,你是否收到了这样的错误提示? - SiegeX
2
@Matijs:我已更新答案,包括close(),应该可以消除你的错误。 - SiegeX
@Matijs,我已经为@SiegeX的额外+1做好了。:) 很棒的答案。 - Mathias Bynens
显示剩余3条评论

0

我对ccs和png文件没有太多经验,但考虑到您非常有帮助的“我有这个字符串,我想要那个字符串”的描述,这是第一版草稿

#! /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'

这里有很多脆弱性

  • 我理解得对吗,是png文件和css文件,对吗?
  • 你知道ksh/bash $1 $2等参数吗?
  • 参数计数的测试没问题,但是
  • 你可能真的想确认$1和$2是否真的存在
  • 没有机会指定输出文件,可以像这样运行

pngReplacer pngFile ccsFile > newCcsFile

  • 如果{ }中的内容让你感到不安,你可以将其全部剪切掉,直接在sed命令的'|'字符之前放置$2。
  • 我会很惊讶如果AIX sed能够处理280个字符的替换表达式,但我手头上没有它来进行测试。一般来说,sed在这方面非常脆弱。
  • 最大的脆弱性是@reg-exp@replace-exp@分隔符(@)。如果你最终在base64Str中得到一个@符号,那么你就必须找到一个不在新base64Str中的字符来使用。

希望这可以帮助你。


0

我编写的命令行可以将HTML文件中包含的所有图像进行编码。

通过编辑base64images函数,它可以轻松地适应于对CSS文件中的所有图像进行编码。

它只需要PHP命令行解释器即可。

#!/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
考虑使用Perl而不是sed。完全跳过awk,它很别扭:-)。(实际上,这是我学习的第一种编程语言之一,但Perl要好得多)。
更好的方法是根本不要做,因为它在50%的用户浏览器中无法工作。无论如何,在以这种方式“告诉他们”之前,请非常确信您不关心那些用户。

从@SiegeX的示例中可以看出,awk确实很笨拙 :) 但是,我希望有一个适合单行命令行的解决方案。不过,我不确定在Mac上是否还可能实现这一点。还是谢谢你的建议。 - Matijs
@Matijs 这有些尴尬,因为如果你知道自己在做什么,就可以通过使用快捷方式使awk代码相当简洁。 我在我的代码中使用了许多这样的快捷方式。 如果我按照冗长的方式来编写它,它实际上是非常易读的; 它看起来很像C代码; 但它会变成两倍长。 - SiegeX

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接