zoukankan      html  css  js  c++  java
  • 将文件内容隐藏在bmp位图中

    首先要实现这个功能,你必须知道bmp位图文件的格式,这里我就不多说了,请看:http://www.cnblogs.com/xiehy/archive/2011/06/07/2074405.html

    接下来主要讲解实现的思路和源码:

    实现思路:
    根据bmp的文件的格式(记录了文件大小,文件数据的位置等信息)和读取文件内容的方式(只读取指定偏移点的数据),
    可得出:当我们改变数据偏移点的值和文件的大小,将要隐藏的文件内容保存在头部到偏移点的区域即可。


    实现步骤:
    1、解析整个文件的格式信息
    2、获取偏移点位置
    3、定位到调色板结束位置,将文件数据插入到调色板结束位置后面
    4、修改偏移位置,加上要隐藏文件的大小
    5、重新写入文件中


    读取文件步骤:
    1、解析bmp文件格式
    2、获取偏移位置end和比特/像素和颜色索引数目
    3、定位到调色板的结束位置,即数据的开始位置start
    4、读取start到end之间的数据到文件中,即为原来文件的内容


    根据上述实现步骤,初步的实现已完成,后期完善某些不足之处,例读取位图信息时使用byte数组存储,
    这样如果文件过大,可能会溢出


    优化:
    1、基本类型的字节的优化,避免强制转换
    2、位图数据可以不存储,在需要写入的时候再去读原文件的位图数据部分
    3、调色板数据在这个方法里也可以不存储,但其实不会很大,所以也没多大关系,可做可不做
    4、抽除掉重复功能的代码


    思考:
    可以直接将文件数据写入到位图数据的最后面?
    可以,这个更加的简单


    实现步骤:
    1、解析总的文件大小
    2、读取bmp所有的数据到新的文件中
    3、读取将要隐藏的文件的内容,写入到新的文件中


    读取文件内容步骤:
    1、解析出原来bmp文件的大小
    2、将输入流读取位置跳到bmp文件尾
    3、读取输入流中剩下的内容,写入到其它文件中即可


    这种实现方式的关键在于解析bmp格式中记录的bmp文件的大小,其它什么都不需要获取,数据的隐藏性较差

    重要源码:

    [java] view plain copy
     
    1. package com.pan.entity;  
    2.   
    3. /** 
    4.  * @author yp2 
    5.  * @date 2015-11-17 
    6.  * @description Bmp文件格式 
    7.  */  
    8. public class Bmp {  
    9.       
    10.     private BmpHeader bmpHeader;  
    11.     private BmpInfoHeader bmpInfoHeader;  
    12.     private BmpPalette bmpPalette;  
    13.     /** 
    14.      * bmp位图数据 
    15.      */  
    16.     private byte[] datas;  
    17.     public BmpHeader getBmpHeader() {  
    18.         return bmpHeader;  
    19.     }  
    20.     public void setBmpHeader(BmpHeader bmpHeader) {  
    21.         this.bmpHeader = bmpHeader;  
    22.     }  
    23.     public BmpInfoHeader getBmpInfoHeader() {  
    24.         return bmpInfoHeader;  
    25.     }  
    26.     public void setBmpInfoHeader(BmpInfoHeader bmpInfoHeader) {  
    27.         this.bmpInfoHeader = bmpInfoHeader;  
    28.     }  
    29.     public BmpPalette getBmpPalette() {  
    30.         return bmpPalette;  
    31.     }  
    32.     public void setBmpPalette(BmpPalette bmpPalette) {  
    33.         this.bmpPalette = bmpPalette;  
    34.     }  
    35.     public byte[] getDatas() {  
    36.         return datas;  
    37.     }  
    38.     public void setDatas(byte[] datas) {  
    39.         this.datas = datas;  
    40.     }  
    41.   
    42. }  



    [java] view plain copy
     
    1. package com.pan.entity;  
    2.   
    3. /** 
    4.  * @author yp2 
    5.  * @date 2015-11-17 
    6.  * @description Bmp文件头部 
    7.  */  
    8. public class BmpHeader {  
    9.       
    10.     /** 
    11.      * 文件的类型,2个字节 
    12.      */  
    13.     private byte[] bfType;    
    14.     /** 
    15.      * 位图文件的大小,字节为单位,4个字节 
    16.      */  
    17.     private byte[] bfSize;    
    18.     /** 
    19.      * 保留,2个字节 
    20.      */  
    21.     private byte[] bfReserved1;  
    22.     /** 
    23.      * 保留,2个字节 
    24.      */  
    25.     private byte[] bfReserved2;  
    26.     /** 
    27.      * 说明从文件开始到实际的图像数据之间的字节的偏移量 
    28.      * 4个字节 
    29.      */  
    30.     private byte[] bfOffBits;  
    31.     public BmpHeader() {  
    32.         bfType = new byte[2];  
    33.         bfSize = new byte[4];  
    34.         bfReserved1 = new byte[2];  
    35.         bfReserved2 = new byte[2];  
    36.         bfOffBits = new byte[4];  
    37.     }  
    38.       
    39.     public byte[] getBfType() {  
    40.         return bfType;  
    41.     }  
    42.     public void setBfType(byte[] bfType) {  
    43.         this.bfType = bfType;  
    44.     }  
    45.     public byte[] getBfSize() {  
    46.         return bfSize;  
    47.     }  
    48.     public void setBfSize(byte[] bfSize) {  
    49.         this.bfSize = bfSize;  
    50.     }  
    51.     public byte[] getBfReserved1() {  
    52.         return bfReserved1;  
    53.     }  
    54.     public void setBfReserved1(byte[] bfReserved1) {  
    55.         this.bfReserved1 = bfReserved1;  
    56.     }  
    57.     public byte[] getBfReserved2() {  
    58.         return bfReserved2;  
    59.     }  
    60.     public void setBfReserved2(byte[] bfReserved2) {  
    61.         this.bfReserved2 = bfReserved2;  
    62.     }  
    63.     public byte[] getBfOffBits() {  
    64.         return bfOffBits;  
    65.     }  
    66.     public void setBfOffBits(byte[] bfOffBits) {  
    67.         this.bfOffBits = bfOffBits;  
    68.     }  
    69.       
    70.       
    71. }  
    [java] view plain copy
     
    1. package com.pan.entity;  
    2.   
    3. /** 
    4.  * @author yp2 
    5.  * @date 2015-11-17 
    6.  * @description Bmp文件信息头部 
    7.  */  
    8. public class BmpInfoHeader {  
    9.       
    10.     /** 
    11.      * 位图信息头部所需要的字数,4个字节 
    12.      */  
    13.     private byte[] biSize;  
    14.     /** 
    15.      * 图像的宽度,像素为单位,4个字节 
    16.      */  
    17.     private byte[] biWidth;  
    18.     /** 
    19.      * 图像的高度,像素为单位,4个字节 
    20.      */  
    21.     private byte[] biHeight;  
    22.     /** 
    23.      * 为目标设备说明颜色平面数,其值将总是设为1,2个字节 
    24.      */  
    25.     private byte[] biPlans;  
    26.     /** 
    27.      * 说明比特数/像素,其值为1、4、8、16、24、32,2个字节 
    28.      */  
    29.     private byte[] biBitCount;  
    30.     /** 
    31.      * 说明图像数据压缩的类型,0 不压缩,4个字节 
    32.      */  
    33.     private byte[] biCompression;  
    34.     /** 
    35.      * 说明图像的大小,字节为单位,当压缩格式为0时,可设置为0,4个字节 
    36.      */  
    37.     private byte[] biSizeImage;  
    38.     /** 
    39.      * 说明水平分辨率,像素/米表示,有符号整数,4个字节 
    40.      */  
    41.     private byte[] biXPelsPerMeter;  
    42.     /** 
    43.      * 说明垂直分辨率,像素/米表示,有符号整数,4个字节 
    44.      */  
    45.     private byte[] biYPelsPerMeter;  
    46.     /** 
    47.      * 说明位图实际使用的彩色表中的颜色索引数,4个字节 
    48.      */  
    49.     private byte[] biClrUsed;  
    50.     /** 
    51.      * 说明对图像显示有重要影响的颜色索引的数目,如果是0,表示都重要 
    52.      * 4个字节 
    53.      */  
    54.     private byte[] biClrImportant;  
    55.     public BmpInfoHeader() {  
    56.         biSize = new byte[4];  
    57.         biWidth = new byte[4];  
    58.         biHeight = new byte[4];  
    59.         biPlans = new byte[2];  
    60.         biBitCount = new byte[2];  
    61.         biCompression = new byte[4];  
    62.         biSizeImage = new byte[4];  
    63.         biXPelsPerMeter = new byte[4];  
    64.         biYPelsPerMeter = new byte[4];  
    65.         biClrUsed = new byte[4];  
    66.         biClrImportant = new byte[4];  
    67.     }  
    68.     public byte[] getBiSize() {  
    69.         return biSize;  
    70.     }  
    71.     public void setBiSize(byte[] biSize) {  
    72.         this.biSize = biSize;  
    73.     }  
    74.     public byte[] getBiWidth() {  
    75.         return biWidth;  
    76.     }  
    77.     public void setBiWidth(byte[] biWidth) {  
    78.         this.biWidth = biWidth;  
    79.     }  
    80.     public byte[] getBiHeight() {  
    81.         return biHeight;  
    82.     }  
    83.     public void setBiHeight(byte[] biHeight) {  
    84.         this.biHeight = biHeight;  
    85.     }  
    86.     public byte[] getBiPlans() {  
    87.         return biPlans;  
    88.     }  
    89.     public void setBiPlans(byte[] biPlans) {  
    90.         this.biPlans = biPlans;  
    91.     }  
    92.     public byte[] getBiBitCount() {  
    93.         return biBitCount;  
    94.     }  
    95.     public void setBiBitCount(byte[] biBitCount) {  
    96.         this.biBitCount = biBitCount;  
    97.     }  
    98.     public byte[] getBiCompression() {  
    99.         return biCompression;  
    100.     }  
    101.     public void setBiCompression(byte[] biCompression) {  
    102.         this.biCompression = biCompression;  
    103.     }  
    104.     public byte[] getBiSizeImage() {  
    105.         return biSizeImage;  
    106.     }  
    107.     public void setBiSizeImage(byte[] biSizeImage) {  
    108.         this.biSizeImage = biSizeImage;  
    109.     }  
    110.     public byte[] getBiXPelsPerMeter() {  
    111.         return biXPelsPerMeter;  
    112.     }  
    113.     public void setBiXPelsPerMeter(byte[] biXPelsPerMeter) {  
    114.         this.biXPelsPerMeter = biXPelsPerMeter;  
    115.     }  
    116.     public byte[] getBiYPelsPerMeter() {  
    117.         return biYPelsPerMeter;  
    118.     }  
    119.     public void setBiYPelsPerMeter(byte[] biYPelsPerMeter) {  
    120.         this.biYPelsPerMeter = biYPelsPerMeter;  
    121.     }  
    122.     public byte[] getBiClrUsed() {  
    123.         return biClrUsed;  
    124.     }  
    125.     public void setBiClrUsed(byte[] biClrUsed) {  
    126.         this.biClrUsed = biClrUsed;  
    127.     }  
    128.     public byte[] getBiClrImportant() {  
    129.         return biClrImportant;  
    130.     }  
    131.     public void setBiClrImportant(byte[] biClrImportant) {  
    132.         this.biClrImportant = biClrImportant;  
    133.     }  
    134.   
    135. }  
    [java] view plain copy
     
    1. package com.pan.entity;  
    2.   
    3. /** 
    4.  * @author yp2 
    5.  * @date 2015-11-17 
    6.  * @description Bmp调色板 
    7.  */  
    8. public class BmpPalette {  
    9.       
    10.     private byte[][] palettes;      //颜色索引映射表  
    11.   
    12.     public byte[][] getPalettes() {  
    13.         return palettes;  
    14.     }  
    15.   
    16.     public void setPalettes(byte[][] palettes) {  
    17.         this.palettes = palettes;  
    18.     }  
    19.       
    20.   
    21. }  



    [java] view plain copy
     
    1. package com.pan.utils;  
    2.   
    3. /** 
    4.  * @author yp2 
    5.  * @date 2015-11-18 
    6.  * @description 字节操作工具 
    7.  */  
    8. public class ByteUtil {  
    9.   
    10.     /** 
    11.      * 将byte数组转换为16进制字符串 
    12.      * <br/> 
    13.      * 实现思路: 
    14.      * 先将byte转换成int,再使用Integer.toHexString(int) 
    15.      * @param data  byte数组 
    16.      * @return 
    17.      */  
    18.     public static String byteToHex(byte[] data, int start, int end) {  
    19.         StringBuilder builder = new StringBuilder();  
    20.         for(int i = start; i < end; i++) {  
    21.             int tmp = data[i] & 0xff;  
    22.             String hv = Integer.toHexString(tmp);  
    23.             if(hv.length() < 2) {  
    24.                 builder.append("0");  
    25.             }  
    26.             builder.append(hv);  
    27.             /*builder.append(" ");*/  
    28.             if(i % 16 == 15) {  
    29.                 /*builder.append(" ");*/  
    30.             }  
    31.         }  
    32.         return builder.toString();  
    33.     }  
    34.       
    35.     /** 
    36.      * 将byte数组转换为16进制字符串(该字符串方便查看) 
    37.      * 输出信息版:16个字节一行显示 
    38.      * @param data 
    39.      * @param start 
    40.      * @param end 
    41.      * @return 
    42.      */  
    43.     public static String byteToHexforPrint(byte[] data, int start, int end) {  
    44.         StringBuilder builder = new StringBuilder();  
    45.         for(int i = start; i < end; i++) {  
    46.             int tmp = data[i] & 0xff;  
    47.             String hv = Integer.toHexString(tmp);  
    48.             if(hv.length() < 2) {  
    49.                 builder.append("0");  
    50.             }  
    51.             builder.append(hv);  
    52.             builder.append(" ");  
    53.             if(i % 16 == 15) {  
    54.                 builder.append(" ");  
    55.             }  
    56.         }  
    57.         return builder.toString();  
    58.     }  
    59.       
    60.     /** 
    61.      * 十六进制字符串转换为字节数组 
    62.      * @param hexStr    十六进制字符串 
    63.      * @return          字节数组 
    64.      */  
    65.     public static byte[] hexToByte(String hexStr) {  
    66.         byte[] datas = new byte[(hexStr.length() - 1) / 2 + 1];  
    67.         hexStr = hexStr.toUpperCase();  
    68.         int pos = 0;  
    69.         for(int i = 0; i < hexStr.length(); i+=2) {  
    70.             if(i + 1 < hexStr.length()) {  
    71.                 datas[pos] = (byte) ((indexOf(hexStr.charAt(i)+"") << 4) + indexOf(hexStr.charAt(i+1)+""));  
    72.             }  
    73.             pos++;  
    74.         }  
    75.         return datas;  
    76.     }  
    77.       
    78.     /** 
    79.      * 计算指定字符串(这里要求是字符)的16进制所表示的数字 
    80.      * @param str 
    81.      * @return 
    82.      */  
    83.     public static int indexOf(String str) {  
    84.         return "0123456789ABCDEF".indexOf(str);  
    85.     }  
    86.       
    87.     /** 
    88.      * 计算byte数组所表示的值,字节数组的值以小端表示,低位在低索引上,高位在高索引 
    89.      * <br/> 
    90.      * 例:data = {1,2},那么结果为: 2 << 8 + 1 = 513 
    91.      * @param data  byte数组 
    92.      * @return      计算出的值 
    93.      */  
    94.     public static long lowByteToLong(byte[] data) {  
    95.         long sum = 0;  
    96.         for(int i = 0; i < data.length; i++) {  
    97.             long value = ((data[i] & 0xff) << (8 * i));  
    98.             sum += value;  
    99.         }  
    100.         return sum;  
    101.     }  
    102.       
    103.     /** 
    104.      * 计算byte数组所表示的值,字节数组的值以大端表示,低位在高索引上,高位在低索引 
    105.      * <br/> 
    106.      * 例:data = {1,2},那么结果为: 1 << 8 + 2 = 258 
    107.      * @param data  byte数组 
    108.      * @return      计算出的值 
    109.      */  
    110.     public static long highByteToLong(byte[] data) {  
    111.         long sum = 0;  
    112.         for(int i = 0; i < data.length; i++) {  
    113.             long value = ((data[i] & 0xff) << (8 * (data.length - i - 1)));  
    114.             sum += value;  
    115.         }  
    116.         return sum;  
    117.     }  
    118.       
    119.     /** 
    120.      * 计算byte数组所表示的值,字节数组的值以小端表示,低位在低索引上,高位在高索引 
    121.      * <br/> 
    122.      * 例:data = {1,2},那么结果为: 2 << 8 + 1 = 513 
    123.      * @param data  byte数组 
    124.      * @return      计算出的值 
    125.      */  
    126.     public static int lowByteToInt(byte[] data) {  
    127.         int sum = 0;  
    128.         for(int i = 0; i < data.length; i++) {  
    129.             long value = ((data[i] & 0xff) << (8 * i));  
    130.             sum += value;  
    131.         }  
    132.         return sum;  
    133.     }  
    134.       
    135.     /** 
    136.      * 计算byte数组所表示的值,字节数组的值以大端表示,低位在高索引上,高位在低索引 
    137.      * <br/> 
    138.      * 例:data = {1,2},那么结果为: 1 << 8 + 2 = 258 
    139.      * @param data  byte数组 
    140.      * @return      计算出的值 
    141.      */  
    142.     public static int highByteToInt(byte[] data) {  
    143.         int sum = 0;  
    144.         for(int i = 0; i < data.length; i++) {  
    145.             long value = ((data[i] & 0xff) << (8 * (data.length - i - 1)));  
    146.             sum += value;  
    147.         }  
    148.         return sum;  
    149.     }  
    150.       
    151.     /** 
    152.      * long值转换为指定长度的小端字节数组 
    153.      * @param data      long值 
    154.      * @param len       长度 
    155.      * @return          字节数组,小端形式展示 
    156.      */  
    157.     public static byte[] longToLowByte(long data, int len) {  
    158.         byte[] value = new byte[len];  
    159.         for(int i = 0; i < len; i++) {  
    160.             value[i] = (byte) ((data >> (8 * i )) & 0xff);  
    161.         }  
    162.         return value;  
    163.     }  
    164.       
    165.     /** 
    166.      * long值转换为指定长度的大端字节数组 
    167.      * @param data      long值 
    168.      * @param len       长度 
    169.      * @return          字节数组,大端形式展示 
    170.      */  
    171.     public static byte[] longToHighByte(long data, int len) {  
    172.         byte[] value = new byte[len];  
    173.         for(int i = 0; i < len; i++) {  
    174.             value[i] = (byte) ((data >> (8 * (len - 1 - i) )) & 0xff);  
    175.         }  
    176.         return value;  
    177.     }  
    178.       
    179.     /** 
    180.      * int值转换为指定长度的小端字节数组 
    181.      * @param data      int值 
    182.      * @param len       长度 
    183.      * @return          字节数组,小端形式展示 
    184.      */  
    185.     public static byte[] intToLowByte(int data, int len) {  
    186.         byte[] value = new byte[len];  
    187.         for(int i = 0; i < len; i++) {  
    188.             value[i] = (byte) ((data >> (8 * i )) & 0xff);  
    189.         }  
    190.         return value;  
    191.     }  
    192.       
    193.     /** 
    194.      * int值转换为指定长度的大端字节数组 
    195.      * @param data      int值 
    196.      * @param len       长度 
    197.      * @return          字节数组,大端形式展示 
    198.      */  
    199.     public static byte[] intToHighByte(int data, int len) {  
    200.         byte[] value = new byte[len];  
    201.         for(int i = 0; i < len; i++) {  
    202.             value[i] = (byte) ((data >> (8 * (len - 1 - i) )) & 0xff);  
    203.         }  
    204.         return value;  
    205.     }  
    206.       
    207.     /** 
    208.      * 计算base的exponent次方 
    209.      * @param base      基数 
    210.      * @param exponent  指数 
    211.      * @return 
    212.      */  
    213.     public static long power(int base, int exponent) {  
    214.         long sum = 1;  
    215.         for(int i = 0; i < exponent; i++) {  
    216.             sum *= base;  
    217.         }  
    218.         return sum;  
    219.     }  
    220.       
    221.     public static void main(String[] args) {  
    222.         byte[] data = new byte[]{1,2};  
    223.         System.out.println(highByteToInt(data));  
    224.         System.out.println(lowByteToInt(data));  
    225.         System.out.println(byteToHex(intToHighByte(258, 4), 0, 4));  
    226.         System.out.println(byteToHex(intToLowByte(258, 4), 0, 4));  
    227.     }  
    228. }  



    [java] view plain copy
     
    1. package com.pan.utils;  
    2.   
    3. import java.io.BufferedInputStream;  
    4. import java.io.File;  
    5. import java.io.FileInputStream;  
    6. import java.io.FileOutputStream;  
    7. import java.io.IOException;  
    8. import java.io.InputStream;  
    9. import java.io.OutputStream;  
    10.   
    11. import com.pan.entity.Bmp;  
    12. import com.pan.entity.BmpHeader;  
    13. import com.pan.entity.BmpInfoHeader;  
    14. import com.pan.entity.BmpPalette;  
    15.   
    16. /** 
    17.  * @author yp2 
    18.  * @date 2015-11-18 
    19.  * @description 重构后的Bmp工具 
    20.  * <br/> 
    21.  * 主要做了几件事: <br/> 
    22.  * 1.位图数据可以不存储,在需要写入的时候再去读原文件的位图数据部分<br/> 
    23.  * 2.抽除掉重复功能的代码<br/> 
    24.  */  
    25. public class BmpUtilRefactoring {  
    26.       
    27.     /** 
    28.      * 读取指定bmp文件的信息到对象中 
    29.      * @param bmpFile       bmp文件路径 
    30.      * @return              代表Bmp文件信息的对象 
    31.      */  
    32.     private static Bmp readBmp(String bmpFile) {  
    33.         Bmp bmp = new Bmp();  
    34.         File file = new File(bmpFile);  
    35.         InputStream in = null;  
    36.         try {  
    37.             in = new BufferedInputStream(new FileInputStream(file));  
    38.             in.mark(0);  
    39.             readBmpHeader(bmp, in);  
    40.               
    41.             long bfOffBits = ByteUtil.lowByteToLong(bmp.getBmpHeader().getBfOffBits());  
    42.             long biSize = ByteUtil.lowByteToLong(bmp.getBmpInfoHeader().getBiSize());  
    43.             long biBitCount = ByteUtil.lowByteToLong(bmp.getBmpInfoHeader().getBiBitCount());  
    44.             int index = (int) (14 + biSize);  
    45.             //重新定位到调色板  
    46.             in.reset();  
    47.             in.skip(index);  
    48.             if(bfOffBits - biSize - 14 == 0) {  
    49.                 //没有调色板  
    50.                 System.out.println(ByteUtil.lowByteToLong(bmp.getBmpInfoHeader().getBiBitCount()) + "位色无调色板");  
    51.             } else {  
    52.                 //有调色板  
    53.                 byte[][] palettes = new byte[(int) ByteUtil.power(2, (int) biBitCount)][4];  
    54.                 for(int i = 0; i < palettes.length && index < bfOffBits; i++) {  
    55.                     in.read(palettes[i], 0, palettes[i].length);  
    56.                     index += palettes[i].length;  
    57.                 }  
    58.                   
    59.                 BmpPalette bmpPalette = new BmpPalette();  
    60.                 bmpPalette.setPalettes(palettes);  
    61.                 bmp.setBmpPalette(bmpPalette);  
    62.             }  
    63.             //记录bmp文件位图数据  
    64.             /* 
    65.             int len = -1; 
    66.             byte[] buf = new byte[1024]; 
    67.             StringBuilder data = new StringBuilder(); 
    68.             while((len = in.read(buf, 0, buf.length)) > 0) { 
    69.                 data.append(ByteUtil.byteToHex(buf,0, len)); 
    70.             } 
    71.             bmp.setDatas(ByteUtil.hexToByte(data.toString()));*/  
    72.               
    73.               
    74.         } catch (IOException e) {  
    75.             e.printStackTrace();  
    76.         } finally {  
    77.             try {  
    78.                 in.close();  
    79.             } catch (IOException e) {  
    80.                 e.printStackTrace();  
    81.             }  
    82.         }  
    83.           
    84.         return bmp;  
    85.     }  
    86.       
    87.     /** 
    88.      * 读取bmp文件输入流的头部信息到Bmp中的头部信息中,要求输入流处于文件的开头 
    89.      * @param bmp               Bmp对象 
    90.      * @param in                bmp文件输入流 
    91.      * @throws IOException 
    92.      */  
    93.     private static void readBmpHeader(Bmp bmp, InputStream in) throws IOException {  
    94.         BmpHeader bmpHeader = new BmpHeader();  
    95.         in.read(bmpHeader.getBfType(), 0, bmpHeader.getBfType().length);  
    96.         in.read(bmpHeader.getBfSize(), 0, bmpHeader.getBfSize().length);  
    97.         in.read(bmpHeader.getBfReserved1(), 0, bmpHeader.getBfReserved1().length);  
    98.         in.read(bmpHeader.getBfReserved2(), 0, bmpHeader.getBfReserved2().length);  
    99.         in.read(bmpHeader.getBfOffBits(), 0, bmpHeader.getBfOffBits().length);  
    100.         bmp.setBmpHeader(bmpHeader);  
    101.           
    102.         BmpInfoHeader bmpInfoHeader = new BmpInfoHeader();  
    103.         in.read(bmpInfoHeader.getBiSize(), 0, bmpInfoHeader.getBiSize().length);  
    104.         in.read(bmpInfoHeader.getBiWidth(), 0, bmpInfoHeader.getBiWidth().length);  
    105.         in.read(bmpInfoHeader.getBiHeight(), 0, bmpInfoHeader.getBiHeight().length);  
    106.         in.read(bmpInfoHeader.getBiPlans(), 0, bmpInfoHeader.getBiPlans().length);  
    107.         in.read(bmpInfoHeader.getBiBitCount(), 0, bmpInfoHeader.getBiBitCount().length);  
    108.         in.read(bmpInfoHeader.getBiCompression(), 0, bmpInfoHeader.getBiCompression().length);  
    109.         in.read(bmpInfoHeader.getBiSizeImage(), 0, bmpInfoHeader.getBiSizeImage().length);  
    110.         in.read(bmpInfoHeader.getBiXPelsPerMeter(), 0, bmpInfoHeader.getBiXPelsPerMeter().length);  
    111.         in.read(bmpInfoHeader.getBiYPelsPerMeter(), 0, bmpInfoHeader.getBiYPelsPerMeter().length);  
    112.         in.read(bmpInfoHeader.getBiClrUsed(), 0, bmpInfoHeader.getBiClrUsed().length);  
    113.         in.read(bmpInfoHeader.getBiClrImportant(), 0, bmpInfoHeader.getBiClrImportant().length);  
    114.         bmp.setBmpInfoHeader(bmpInfoHeader);  
    115.     }  
    116.       
    117.     /** 
    118.      * 写入要隐藏文件的内容和原Bmp文件信息到指定数据文件中 
    119.      * @param bmp               原Bmp文件信息 
    120.      * @param inputFileName     要隐藏的文件 
    121.      * @param outFileName       输出的文件 
    122.      * @throws IOException  
    123.      */  
    124.     private static void writeFileToBmp(Bmp bmp, String bmpFileName, String inputFileName, String outFileName) throws IOException {  
    125.         File inputFile = new File(inputFileName);  
    126.         File outFile = new File(outFileName);  
    127.         File bmpFile = new File(bmpFileName);  
    128.         if(!outFile.exists()) {  
    129.             outFile.createNewFile();  
    130.         }  
    131.         //记录原来bmp文件的数据偏移位置  
    132.         long oldbfOffBits = ByteUtil.lowByteToLong(bmp.getBmpHeader().getBfOffBits());  
    133.         //计算出新的数据偏移位置:= 原来的偏移位置 + 要隐藏文件的总字节数   
    134.         long bfOffBits = inputFile.length() + ByteUtil.lowByteToLong(bmp.getBmpHeader().getBfOffBits());  
    135.         //设置新的数据偏移位置,以便写入新的文件中  
    136.         bmp.getBmpHeader().setBfOffBits(ByteUtil.longToLowByte(bfOffBits, 4));  
    137.           
    138.         InputStream in = null;  
    139.         InputStream bmpIn = null;  
    140.         OutputStream out = null;  
    141.           
    142.         try {  
    143.             in = new FileInputStream(inputFile);  
    144.             bmpIn = new BufferedInputStream(new FileInputStream(bmpFile));  
    145.             out = new FileOutputStream(outFile);  
    146.             //将bmp头部信息写入输入流中  
    147.             writeBmpHeader(bmp, out);  
    148.             //写入要隐藏的文件内容  
    149.             int len = -1;  
    150.             byte[] buf = new byte[1024];  
    151.             while((len = in.read(buf)) > 0) {  
    152.                 out.write(buf, 0, len);  
    153.             }  
    154.             //跳过头部和调色板信息  
    155.             bmpIn.skip(oldbfOffBits);  
    156.             len = -1;  
    157.             //写入原有位图数据  
    158.             while((len = bmpIn.read(buf)) > 0) {  
    159.                 out.write(buf, 0, len);  
    160.             }  
    161.         } catch (Exception e) {  
    162.             e.printStackTrace();  
    163.         } finally {  
    164.             try {  
    165.                 in.close();  
    166.                 out.close();  
    167.                 bmpIn.close();  
    168.             } catch (IOException e) {  
    169.                 e.printStackTrace();  
    170.             }  
    171.         }  
    172.     }  
    173.       
    174.     /** 
    175.      * 将文件内容写入到指定的位图文件内,并改变输出文件名 
    176.      * @param bmpFileName       位图文件名 
    177.      * @param inputFileName     要隐藏的文件名 
    178.      * @param outFileName       输出文件名 
    179.      * @throws IOException 
    180.      */  
    181.     public static void writeFileToBmpFile(String bmpFileName, String inputFileName, String outFileName) throws IOException {  
    182.         Bmp bmp = readBmp(bmpFileName);  
    183.         writeFileToBmp(bmp, bmpFileName, inputFileName, outFileName);  
    184.     }  
    185.       
    186.     /** 
    187.      * 读取bmp文件中隐藏的文件内容到指定的输出文件中去 
    188.      * @param bmpFileName       bmp文件名       
    189.      * @param outFileName       输出文件名 
    190.      * @throws IOException 
    191.      */  
    192.     public static void readFileFromBmpFile(String bmpFileName, String outFileName) throws IOException {  
    193.         File bmpFile = new File(bmpFileName);  
    194.         File outFile = new File(outFileName);  
    195.         Bmp bmp = new Bmp();  
    196.         if(!outFile.exists()) {  
    197.             outFile.createNewFile();  
    198.         }  
    199.         InputStream in = null;  
    200.         OutputStream out = null;  
    201.         int len = -1;  
    202.         try {  
    203.             in = new BufferedInputStream(new FileInputStream(bmpFile));  
    204.             out = new FileOutputStream(outFile);  
    205.             //标记当前输入流位置,方便后面reset跳转到当前输入流读取位置  
    206.             in.mark(0);  
    207.             //读取输入流中包含的头部信息  
    208.             readBmpHeader(bmp, in);  
    209.             //数据偏移位置  
    210.             long bfOffBits = ByteUtil.lowByteToLong(bmp.getBmpHeader().getBfOffBits());  
    211.             //使用的颜色索引数目  
    212.             long biClrUsed = ByteUtil.lowByteToLong(bmp.getBmpInfoHeader().getBiClrUsed());  
    213.             //位图信息头部字节数  
    214.             long biSize = ByteUtil.lowByteToLong(bmp.getBmpInfoHeader().getBiSize());  
    215.             //比特/像素  
    216.             long biBitCount = ByteUtil.lowByteToLong(bmp.getBmpInfoHeader().getBiBitCount());  
    217.             //重置到mark标记的位置,这里是跳转到输入流的开头  
    218.             in.reset();  
    219.             //保存当前文件输入流位置(字节位置)  
    220.             long sumLen = 0;  
    221.             if(biBitCount < 24) {  
    222.                 if(biClrUsed == 0) {  
    223.                     //索引全部都重要  
    224.                     //跳过输入流中的54 + 调色板所占字节数 个字节,这样其实就跳转到了保存隐藏文件内容的位置  
    225.                     in.skip(14 + biSize + ByteUtil.power(2, (int) biBitCount) * 4);  
    226.                     sumLen = 14 + biSize + ByteUtil.power(2, (int) biBitCount) * 4;  
    227.                 } else {  
    228.                     //部分重要  
    229.                     in.skip(14 + biSize + biClrUsed * 4);  
    230.                     sumLen = 14 + biSize + biClrUsed * 4;  
    231.                 }  
    232.                   
    233.             } else {  
    234.                 //没有调色板  
    235.                 in.skip(14 + biSize);  
    236.                 sumLen = 14 + biSize;  
    237.             }  
    238.             byte[] buf = new byte[1024];  
    239.             while((len = in.read(buf)) > 0) {  
    240.                 if((sumLen + len) > bfOffBits) {  
    241.                     //如果超过了数据偏移位置,则截取剩余的字节进行保存  
    242.                     out.write(buf, 0, (int) (bfOffBits - sumLen));  
    243.                     break;  
    244.                 } else {  
    245.                     //没有超过数据偏移位置,则截取读取到的字节  
    246.                     out.write(buf, 0, len);  
    247.                 }  
    248.                 sumLen += len;  
    249.             }  
    250.         } catch (Exception e) {  
    251.             e.printStackTrace();  
    252.             in.close();  
    253.             out.close();  
    254.         }  
    255.     }  
    256.       
    257.     /** 
    258.      * 将bmp头部信息和调色板信息写入输入流中 
    259.      * @param out 
    260.      * @param bmp 
    261.      * @throws IOException 
    262.      */  
    263.     private static void writeBmpHeader(Bmp bmp, OutputStream out) throws IOException {  
    264.         BmpHeader bmpHeader = bmp.getBmpHeader();  
    265.         out.write(bmpHeader.getBfType());  
    266.         out.write(bmpHeader.getBfSize());  
    267.         out.write(bmpHeader.getBfReserved1());  
    268.         out.write(bmpHeader.getBfReserved2());  
    269.         out.write(bmpHeader.getBfOffBits());  
    270.           
    271.         BmpInfoHeader bmpInfoHeader = bmp.getBmpInfoHeader();  
    272.         out.write(bmpInfoHeader.getBiSize());  
    273.         out.write(bmpInfoHeader.getBiWidth());  
    274.         out.write(bmpInfoHeader.getBiHeight());  
    275.         out.write(bmpInfoHeader.getBiPlans());  
    276.         out.write(bmpInfoHeader.getBiBitCount());  
    277.         out.write(bmpInfoHeader.getBiCompression());  
    278.         out.write(bmpInfoHeader.getBiSizeImage());  
    279.         out.write(bmpInfoHeader.getBiXPelsPerMeter());  
    280.         out.write(bmpInfoHeader.getBiYPelsPerMeter());  
    281.         out.write(bmpInfoHeader.getBiClrUsed());  
    282.         out.write(bmpInfoHeader.getBiClrImportant());  
    283.           
    284.         BmpPalette bmpPalette = bmp.getBmpPalette();  
    285.         if(bmpPalette != null && bmpPalette.getPalettes() != null) {  
    286.             for(int i = 0; i < bmpPalette.getPalettes().length; i++) {  
    287.                 out.write(bmpPalette.getPalettes()[i]);  
    288.             }  
    289.         }  
    290.           
    291.     }  
    292.       
    293.       
    294.   
    295. }  
    [java] view plain copy
     
    1. package com.pan.main;  
    2.   
    3. import java.io.IOException;  
    4.   
    5. import com.pan.utils.BmpUtilRefactoring;  
    6.   
    7. public class Main {  
    8.   
    9.     public static void main(String[] args) throws IOException {  
    10.           
    11.         /*1位色*/  
    12.         BmpUtilRefactoring.writeFileToBmpFile(Main.class.getClassLoader().getResource("resource/SmallConfetti.bmp").getPath(),  
    13.                 Main.class.getClassLoader().getResource("resource/SmallConfettiscrect.txt").getPath(),  
    14.                 Main.class.getClassLoader().getResource("resource/").getPath() + "SmallConfettiout.bmp");  
    15.           
    16.         BmpUtilRefactoring.readFileFromBmpFile(Main.class.getClassLoader().getResource("resource/").getPath() + "SmallConfettiout.bmp",  
    17.                 Main.class.getClassLoader().getResource("resource/").getPath() + "SmallConfettiscrectout.txt");  
    18.       
    19.         /*4位色*/  
    20.         BmpUtilRefactoring.writeFileToBmpFile(Main.class.getClassLoader().getResource("resource/verisign.bmp").getPath(),  
    21.                 Main.class.getClassLoader().getResource("resource/verisignscrect.txt").getPath(),  
    22.                 Main.class.getClassLoader().getResource("resource/").getPath() + "verisignout.bmp");  
    23.           
    24.         BmpUtilRefactoring.readFileFromBmpFile(Main.class.getClassLoader().getResource("resource/").getPath() + "verisignout.bmp",  
    25.                 Main.class.getClassLoader().getResource("resource/").getPath() + "verisignscrectout.txt");  
    26.       
    27.         BmpUtilRefactoring.writeFileToBmpFile(Main.class.getClassLoader().getResource("resource/srun.bmp").getPath(),  
    28.                 Main.class.getClassLoader().getResource("resource/srunscrect.txt").getPath(),  
    29.                 Main.class.getClassLoader().getResource("resource/").getPath() + "srunout.bmp");  
    30.         BmpUtilRefactoring.readFileFromBmpFile(Main.class.getClassLoader().getResource("resource/").getPath() + "srunout.bmp",  
    31.                 Main.class.getClassLoader().getResource("resource/").getPath() + "srunscrectout.txt");  
    32.           
    33.         /*8位色*/  
    34.         BmpUtilRefactoring.writeFileToBmpFile(Main.class.getClassLoader().getResource("resource/SplashScreen.bmp").getPath(),  
    35.                 Main.class.getClassLoader().getResource("resource/SplashScreenscrect.txt").getPath(),  
    36.                 Main.class.getClassLoader().getResource("resource/").getPath() + "SplashScreenout.bmp");  
    37.           
    38.         BmpUtilRefactoring.readFileFromBmpFile(Main.class.getClassLoader().getResource("resource/").getPath() + "SplashScreenout.bmp",  
    39.                 Main.class.getClassLoader().getResource("resource/").getPath() + "SplashScreenscrectout.txt");  
    40.           
    41.         /*24位色*/  
    42.         BmpUtilRefactoring.writeFileToBmpFile(Main.class.getClassLoader().getResource("resource/background.bmp").getPath(),  
    43.                 Main.class.getClassLoader().getResource("resource/backgroundscrect.txt").getPath(),  
    44.                 Main.class.getClassLoader().getResource("resource/").getPath() + "backgroundout.bmp");  
    45.           
    46.         BmpUtilRefactoring.readFileFromBmpFile(Main.class.getClassLoader().getResource("resource/").getPath() + "backgroundout.bmp",  
    47.                 Main.class.getClassLoader().getResource("resource/").getPath() + "backgroundscrectout.txt");  
    48.           
    49.         /*32位色*/  
    50.         BmpUtilRefactoring.writeFileToBmpFile(Main.class.getClassLoader().getResource("resource/WindowsMail.bmp").getPath(),  
    51.                 Main.class.getClassLoader().getResource("resource/WindowsMailscrect.txt").getPath(),  
    52.                 Main.class.getClassLoader().getResource("resource/").getPath() + "WindowsMailout.bmp");  
    53.           
    54.         BmpUtilRefactoring.readFileFromBmpFile(Main.class.getClassLoader().getResource("resource/").getPath() + "WindowsMailout.bmp",  
    55.                 Main.class.getClassLoader().getResource("resource/").getPath() + "WindowsMailscrectout.txt");  
    56.           
    57.           
    58.     }  
    59.   
    60. }  


    代码下载:搓http://download.csdn.net/detail/u012009613/9280153

    转载请注明出处,谢谢!

  • 相关阅读:
    基数排序学习
    桶排序学习
    计数排序-不基于比较O(n)
    基尼系数
    拉普拉斯进行特征选择
    int ,long long等范围
    Codeforces780C
    51 Nod 1119
    字典树入门
    POJ 2531 暴力深搜
  • 原文地址:https://www.cnblogs.com/csguo/p/8110518.html
Copyright © 2011-2022 走看看