gzip压缩引起的热更新bug

就在昨天,一切都是那么的寻常,就是普通得打了一次热更新包,然后上传到服务器,然后…

bug
啊,什么情况呀,七百多兆呀,整个资源也才这么七百多兆,什么鬼~~~,冷静,一定要保持冷静。

查找过程

project.minifest

  • 推断 :cocos creator 热更新是基于对比资源列表 project.minifest 的,既然这么大,应该是资源列表里面的文件 md5 全部都不一样
  • 验证: 打开两个版本的 project.minifest ,对比文件的 md5 ,确实不一样,推断正确

对比资源

  • 推荐:既然md5不一样,那么应该是生成的资源就不同,否则就是生成md5过程出bug了;
  • 验证:使用 beyond compare 对比两边的资源,两边的资源真的是不一样的,等等,对比发现,只要是图片资源,全都不一样了,锁定重点——图片

原始资源

  • 推断:为什么图片 md5 值会不一样呢?难道图片文件在构建之后都被修改过了
  • 验证:新建了一个hello world工程,前后打包两次,对比构建之后的图片,发现是一样的,推断错误

纹理压缩

  • 推断:既然构建图片打包之后是没有变,难道是纹理压缩引起的?
  • 验证:将hello world工程中的图片,选择纹理压缩格式,再次打包对比,是一致的,推断错误

资源加密

  • 推断:除了纹理压缩之外,构建过程中,还对资源进行了加密,难道是加密导致的吗?
  • 验证:将上面压缩之后的纹理,先后加密,内容还是无差别,推断错误

压缩

  • 推断:构建之后除了加密,就只有gzip压缩了,难道是压缩文件导致?
  • 验证:将上面加密之后的纹理,先后压缩,真的不一样了。。。

注:其实开始我是在同一个脚本中压缩两次,并没有发现不一样

追本溯源

不同时间压缩的同样的内容,产生的压缩文件竟然不一样,为什么呢?查资料中…

文件格式说明:
10字节的头,包含幻数、版本号以及时间戳
可选的扩展头,如原文件名
文件体,包括DEFLATE压缩的数据
8字节的尾注,包括CRC-32校验和以及未压缩的原始数据长度
——百度百科

压缩过程中用到了时间戳,不同时间压缩久会导致内容不一样的呀

解决方案

纹理压缩用的是 py 脚本实现了,使用的是 gnuzip 库,去查了这个库的文档.

def open(filename, mode="rb", compresslevel=9):
    """Shorthand for GzipFile(filename, mode, compresslevel).

    The filename argument is required; mode defaults to 'rb'
    and compresslevel defaults to 9.

    """
    return GzipFile(filename, mode, compresslevel)

class GzipFile(io.BufferedIOBase):
    """The GzipFile class simulates most of the methods of a file object with
    the exception of the readinto() and truncate() methods.

    """
  
    myfileobj = None
    max_read_chunk = 10 * 1024 * 1024   # 10Mb

    def __init__(self, filename=None, mode=None,compresslevel=9, fileobj=None, mtime=None):
    """Constructor for the GzipFile class.

        At least one of fileobj and filename must be given a
        non-trivial value.

        The new class instance is based on fileobj, which can be a regular
        file, a StringIO object, or any other object which simulates a file.
        It defaults to None, in which case filename is opened to provide
        a file object.

        When fileobj is not None, the filename argument is only used to be
        included in the gzip file header, which may include the original
        filename of the uncompressed file.  It defaults to the filename of
        fileobj, if discernible; otherwise, it defaults to the empty string,
        and in this case the original filename is not included in the header.

        The mode argument can be any of 'r', 'rb', 'a', 'ab', 'w', or 'wb',
        depending on whether the file will be read or written.  The default
        is the mode of fileobj if discernible; otherwise, the default is 'rb'.
        Be aware that only the 'rb', 'ab', and 'wb' values should be used
        for cross-platform portability.

        The compresslevel argument is an integer from 0 to 9 controlling the
        level of compression; 1 is fastest and produces the least compression,
        and 9 is slowest and produces the most compression. 0 is no compression
        at all. The default is 9.

        The mtime argument is an optional numeric timestamp to be written
        to the stream when compressing.  All gzip compressed streams
        are required to contain a timestamp.  If omitted or None, the
        current time is used.  This module ignores the timestamp when
        decompressing; however, some programs, such as gunzip, make use
        of it.  The format of the timestamp is the same as that of the
        return value of time.time() and of the st_mtime member of the
        object returned by os.stat().

        """
	# .... 其他省略

调用的是 open 函数,没有时间戳
往下找,GzipFile 的构造函数中有一个 mtime,应该就是他了。
最终,弃用 open 函数,新建一个 GzipFile 对象,传入固定的 mtime值。
验证结果,把加密之后的资源,先后压缩,嗯嗯。。。 终于一致了。
重新打热更新包测试,okay了

ok

总结

查找bug的过程,就是不断地推断,然后验证推断的流程。但实际上,过程都是流程要曲折,比如这次如何定位到是图片资源不一致导致的,验证压缩的过程,如果是在同一个脚本都压缩两次,结果就会是一样的,这些一是靠仔细观察,二就是考虑下两个环境有什么不一致的地方。

最后,我是寒风,欢迎大家加Q群(830756115)讨论。
qq

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值