在python脚本中嵌入资源

时间:2016-09-05 20:58:45

标签: python embedded-resource

我想弄清楚如何在python脚本中嵌入二进制内容。例如,我不想要任何外部文件(图像,声音......),我希望所有这些内容都存在于我的python脚本中。

澄清一点的例子,让我们说我得到了这个小片段:

from StringIO import StringIO
from PIL import Image, ImageFilter

embedded_resource = StringIO(open("Lenna.png", "rb").read())
im = Image.open(embedded_resource)
im.show()

im_sharp = im.filter(ImageFilter.SHARPEN)
im_sharp.show()

如您所见,示例是阅读外部文件' Lenna.png'

enter image description here

问题

如何进行嵌入" Lenna.png"作为我的python脚本的资源(变量)。使用python实现这个简单任务的最快方法是什么?

2 个答案:

答案 0 :(得分:4)

解决此问题的最佳方法是将您的图片转换为python字符串,并将其放在一个名为resources.py的单独文件中,然后您只需解析它。

如果您希望将整个内容嵌入到单个二进制文件中,那么您需要查看py2exe之类的内容。 Here是嵌入外部文件的示例

在第一个场景中,您甚至可以使用base64来(de)对图片进行编码,如下所示:

import base64
file = open('yourImage.png'); 
encoded = base64.b64encode(file.read())
data = base64.b64decode(encoded) # Don't forget to file.close() !

答案 1 :(得分:1)

您可能会发现以下类对于在程序中嵌入资源非常有用。要使用它,请使用指向要嵌入的文件的路径调用package方法。该类将打印出DATA属性,该属性应该用于替换已在类中找到的属性。如果要将文件添加到预建数据,请改用add方法。要在程序中使用该类,请使用上下文管理器语法调用load方法。返回的值是Path对象,可以用作其他函数的文件名参数,也可以用于直接加载重构文件。有关示例用法,请参阅此SMTP Client

import base64
import contextlib
import pathlib
import pickle
import pickletools
import sys
import zlib


class Resource:

    """Manager for resources that would normally be held externally."""

    WIDTH = 76
    __CACHE = None
    DATA = b''

    @classmethod
    def package(cls, *paths):
        """Creates a resource string to be copied into the class."""
        cls.__generate_data(paths, {})

    @classmethod
    def add(cls, *paths):
        """Include paths in the pre-generated DATA block up above."""
        cls.__preload()
        cls.__generate_data(paths, cls.__CACHE.copy())

    @classmethod
    def __generate_data(cls, paths, buffer):
        """Load paths into buffer and output DATA code for the class."""
        for path in map(pathlib.Path, paths):
            if not path.is_file():
                raise ValueError('{!r} is not a file'.format(path))
            key = path.name
            if key in buffer:
                raise KeyError('{!r} has already been included'.format(key))
            with path.open('rb') as file:
                buffer[key] = file.read()
        pickled = pickle.dumps(buffer, pickle.HIGHEST_PROTOCOL)
        optimized = pickletools.optimize(pickled)
        compressed = zlib.compress(optimized, zlib.Z_BEST_COMPRESSION)
        encoded = base64.b85encode(compressed)
        cls.__print("    DATA = b'''")
        for offset in range(0, len(encoded), cls.WIDTH):
            cls.__print("\\\n" + encoded[
                slice(offset, offset + cls.WIDTH)].decode('ascii'))
        cls.__print("'''")

    @staticmethod
    def __print(line):
        """Provides alternative printing interface for simplicity."""
        sys.stdout.write(line)
        sys.stdout.flush()

    @classmethod
    @contextlib.contextmanager
    def load(cls, name, delete=True):
        """Dynamically loads resources and makes them usable while needed."""
        cls.__preload()
        if name not in cls.__CACHE:
            raise KeyError('{!r} cannot be found'.format(name))
        path = pathlib.Path(name)
        with path.open('wb') as file:
            file.write(cls.__CACHE[name])
        yield path
        if delete:
            path.unlink()

    @classmethod
    def __preload(cls):
        """Warm up the cache if it does not exist in a ready state yet."""
        if cls.__CACHE is None:
            decoded = base64.b85decode(cls.DATA)
            decompressed = zlib.decompress(decoded)
            cls.__CACHE = pickle.loads(decompressed)

    def __init__(self):
        """Creates an error explaining class was used improperly."""
        raise NotImplementedError('class was not designed for instantiation')