V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
asanelder
V2EX  ›  问与答

Java 读取二进制文件, 打包前执行和打包后执行读出来的 size 不一样?

  •  
  •   asanelder · 2022-01-05 17:44:37 +08:00 · 869 次点击
    这是一个创建于 1053 天前的主题,其中的信息可能已经有所发展或是发生改变。

    读取代码如下

    InputStream in = new ClassPathResource("binary_file.dat").getInputStream();
    out = new ByteArrayOutputStream();
    byte[] b = new byte[1024];
    while(in.read(b) != -1){
        out.write(b);
    }
    
    log.info("data size = {}", out.size());
    

    俺使用两种方式执行

    1. 打包前执行
    2. 打包成 jar 包执行

    发现两种方式的 size 不一样.

    文件大小在 28M 左右.

    奇怪啊~~~

    11 条回复    2022-01-06 10:29:04 +08:00
    asanelder
        1
    asanelder  
    OP
       2022-01-05 17:57:25 +08:00
    神奇啊, 使用 guava 中的工具类来读取就没这个问题了...

    in = new ClassPathResource("binary_file.dat").getInputStream();
    data = ByteStreams.toByteArray(in);
    log.info("data size = {}", out.size());
    AoEiuV020CN
        2
    AoEiuV020CN  
       2022-01-05 18:05:29 +08:00   ❤️ 1
    首先,代码有误,read 会返回长度 length ,write 时应当只 write 一个范围 0, length ,
    asanelder
        3
    asanelder  
    OP
       2022-01-05 18:17:17 +08:00
    @AoEiuV020CN #2 就算有误的话, 相同的代码, 相同的文件, 一个是打包后执行, 一个是打包前执行, 按道理说, 应该结果也是一样的吧
    AoEiuV020CN
        4
    AoEiuV020CN  
       2022-01-05 18:25:07 +08:00
    @asanelder #3 具体原因我不知道,read 方法很容易涉及到框架内部或者 jvm 内部,要说直接运行和打包后运行保证这些内部环境完全一样我看也不能保证,没准当时内存占用多了一比特就导致不同的缓存处理进而导致其他其他不同,
    不是很想深究错误的代码为什么会引发另一个错误,
    codehz
        5
    codehz  
       2022-01-05 18:46:58 +08:00
    @asanelder 打包之后被压缩了啊,然后就需要边解压边读取,zip 压缩也不是固定输出大小的压缩,解压的时候也很难预测要读多少内容才能得到 1024 ,通常的思路就是读固定大小,然后解出多少就提供多少(
    Jwyt
        6
    Jwyt  
       2022-01-05 19:05:50 +08:00
    @codehz 楼主说的是打 jar 包,不是被读的文件


    不过代码确实有问题,应该是
    (len=in.read(b)) !=-1;
    out.write(b,0,len);
    当然为啥会不一样我也不清楚,楼主可以把代码改了之后再打包试试
    AoEiuV020CN
        7
    AoEiuV020CN  
       2022-01-05 19:26:41 +08:00   ❤️ 1
    @Jwyt #6 应该就是打包压缩的问题,楼主代码中的 ClassPathResource 读取的文件是会被打包到 jar 里的,
    边解压边读取导致无法指定读取 1024 字节,可能比如一次解压出了 500 个字节,read 就直接返回 500 ,下次 read 再解压读取剩下的,
    GuuJiang
        8
    GuuJiang  
       2022-01-05 19:39:27 +08:00   ❤️ 1
    @asanelder 你这样读到 out 里的内容都是错误的,更别说长度了,一次 read 可能小于 1024 ,但是每次 write 都是写入整个 b 数组,导致多出了一些垃圾数据
    Jwyt
        9
    Jwyt  
       2022-01-05 20:30:27 +08:00
    @AoEiuV020CN 对 我忘了这一茬了
    asanelder
        10
    asanelder  
    OP
       2022-01-06 09:50:52 +08:00
    @Jwyt #9
    @GuuJiang #8
    @AoEiuV020CN #7
    @Jwyt #6
    @codehz #5

    感谢老铁们的耐心回答, 俺抽时间试试, 有了结果给各位说一声
    asanelder
        11
    asanelder  
    OP
       2022-01-06 10:29:04 +08:00
    @AoEiuV020CN #4
    @codehz #5
    @Jwyt #6
    @AoEiuV020CN #7
    @GuuJiang #8

    嗯, 确实是代码有问题, 俺按照铁子们的说法修改后

    byte[] b = new byte[1024];
    int len = in.read(b);
    while(len != -1){
    out.write(b, 0, len);
    len = in.read(b);
    }
    log.info("data size = {}", data.length);

    就正常了

    至于具体原因~~~ 看样子还是得按照文档正确调用接口, 这样其行为才和预期一样. 至于之前没有指定 length 导致在 jar 中和非 jar 中形为不一致. 就忽略吧
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2838 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 14:31 · PVG 22:31 · LAX 06:31 · JFK 09:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.