將文件內容隱藏在bmp位圖中

首先要實現這個功能,你必須知道bmp位圖文件的格式,這裏我就很少說了,請看:http://www.cnblogs.com/xiehy/archive/2011/06/07/2074405.htmlhtml

接下來主要講解實現的思路和源碼:java

實現思路:
根據bmp的文件的格式(記錄了文件大小,文件數據的位置等信息)和讀取文件內容的方式(只讀取指定偏移點的數據),
可得出:當咱們改變數據偏移點的值和文件的大小,將要隱藏的文件內容保存在頭部到偏移點的區域便可。


實現步驟:
一、解析整個文件的格式信息
二、獲取偏移點位置
三、定位到調色板結束位置,將文件數據插入到調色板結束位置後面
四、修改偏移位置,加上要隱藏文件的大小
五、從新寫入文件中


讀取文件步驟:
一、解析bmp文件格式
二、獲取偏移位置end和比特/像素和顏色索引數目
三、定位到調色板的結束位置,即數據的開始位置start
四、讀取start到end之間的數據到文件中,即爲原來文件的內容


根據上述實現步驟,初步的實現已完成,後期完善某些不足之處,例讀取位圖信息時使用byte數組存儲,
這樣若是文件過大,可能會溢出


優化:
一、基本類型的字節的優化,避免強制轉換
二、位圖數據能夠不存儲,在須要寫入的時候再去讀原文件的位圖數據部分
三、調色板數據在這個方法裏也能夠不存儲,但其實不會很大,因此也沒多大關係,可作可不作
四、抽除掉重複功能的代碼


思考:
能夠直接將文件數據寫入到位圖數據的最後面?
能夠,這個更加的簡單


實現步驟:
一、解析總的文件大小
二、讀取bmp全部的數據到新的文件中
三、讀取將要隱藏的文件的內容,寫入到新的文件中


讀取文件內容步驟:
一、解析出原來bmp文件的大小
二、將輸入流讀取位置跳到bmp文件尾
三、讀取輸入流中剩下的內容,寫入到其它文件中便可


這種實現方式的關鍵在於解析bmp格式中記錄的bmp文件的大小,其它什麼都不須要獲取,數據的隱藏性較差數組

 

重要源碼:app

 

 

[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六、2四、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("\n");*/  
  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("\n");  
  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工具

轉載請註明出處,謝謝!優化

相關文章
相關標籤/搜索