首先要实现这个功能,你必须知道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文件的大小,其它什么都不需要获取,数据的隐藏性较差
重要源码:
- package com.pan.entity;
- /**
- * @author yp2
- * @date 2015-11-17
- * @description Bmp文件格式
- */
- public class Bmp {
- private BmpHeader bmpHeader;
- private BmpInfoHeader bmpInfoHeader;
- private BmpPalette bmpPalette;
- /**
- * bmp位图数据
- */
- private byte[] datas;
- public BmpHeader getBmpHeader() {
- return bmpHeader;
- }
- public void setBmpHeader(BmpHeader bmpHeader) {
- this.bmpHeader = bmpHeader;
- }
- public BmpInfoHeader getBmpInfoHeader() {
- return bmpInfoHeader;
- }
- public void setBmpInfoHeader(BmpInfoHeader bmpInfoHeader) {
- this.bmpInfoHeader = bmpInfoHeader;
- }
- public BmpPalette getBmpPalette() {
- return bmpPalette;
- }
- public void setBmpPalette(BmpPalette bmpPalette) {
- this.bmpPalette = bmpPalette;
- }
- public byte[] getDatas() {
- return datas;
- }
- public void setDatas(byte[] datas) {
- this.datas = datas;
- }
- }
- package com.pan.entity;
- /**
- * @author yp2
- * @date 2015-11-17
- * @description Bmp文件头部
- */
- public class BmpHeader {
- /**
- * 文件的类型,2个字节
- */
- private byte[] bfType;
- /**
- * 位图文件的大小,字节为单位,4个字节
- */
- private byte[] bfSize;
- /**
- * 保留,2个字节
- */
- private byte[] bfReserved1;
- /**
- * 保留,2个字节
- */
- private byte[] bfReserved2;
- /**
- * 说明从文件开始到实际的图像数据之间的字节的偏移量
- * 4个字节
- */
- private byte[] bfOffBits;
- public BmpHeader() {
- bfType = new byte[2];
- bfSize = new byte[4];
- bfReserved1 = new byte[2];
- bfReserved2 = new byte[2];
- bfOffBits = new byte[4];
- }
- public byte[] getBfType() {
- return bfType;
- }
- public void setBfType(byte[] bfType) {
- this.bfType = bfType;
- }
- public byte[] getBfSize() {
- return bfSize;
- }
- public void setBfSize(byte[] bfSize) {
- this.bfSize = bfSize;
- }
- public byte[] getBfReserved1() {
- return bfReserved1;
- }
- public void setBfReserved1(byte[] bfReserved1) {
- this.bfReserved1 = bfReserved1;
- }
- public byte[] getBfReserved2() {
- return bfReserved2;
- }
- public void setBfReserved2(byte[] bfReserved2) {
- this.bfReserved2 = bfReserved2;
- }
- public byte[] getBfOffBits() {
- return bfOffBits;
- }
- public void setBfOffBits(byte[] bfOffBits) {
- this.bfOffBits = bfOffBits;
- }
- }
- package com.pan.entity;
- /**
- * @author yp2
- * @date 2015-11-17
- * @description Bmp文件信息头部
- */
- public class BmpInfoHeader {
- /**
- * 位图信息头部所需要的字数,4个字节
- */
- private byte[] biSize;
- /**
- * 图像的宽度,像素为单位,4个字节
- */
- private byte[] biWidth;
- /**
- * 图像的高度,像素为单位,4个字节
- */
- private byte[] biHeight;
- /**
- * 为目标设备说明颜色平面数,其值将总是设为1,2个字节
- */
- private byte[] biPlans;
- /**
- * 说明比特数/像素,其值为1、4、8、16、24、32,2个字节
- */
- private byte[] biBitCount;
- /**
- * 说明图像数据压缩的类型,0 不压缩,4个字节
- */
- private byte[] biCompression;
- /**
- * 说明图像的大小,字节为单位,当压缩格式为0时,可设置为0,4个字节
- */
- private byte[] biSizeImage;
- /**
- * 说明水平分辨率,像素/米表示,有符号整数,4个字节
- */
- private byte[] biXPelsPerMeter;
- /**
- * 说明垂直分辨率,像素/米表示,有符号整数,4个字节
- */
- private byte[] biYPelsPerMeter;
- /**
- * 说明位图实际使用的彩色表中的颜色索引数,4个字节
- */
- private byte[] biClrUsed;
- /**
- * 说明对图像显示有重要影响的颜色索引的数目,如果是0,表示都重要
- * 4个字节
- */
- private byte[] biClrImportant;
- public BmpInfoHeader() {
- biSize = new byte[4];
- biWidth = new byte[4];
- biHeight = new byte[4];
- biPlans = new byte[2];
- biBitCount = new byte[2];
- biCompression = new byte[4];
- biSizeImage = new byte[4];
- biXPelsPerMeter = new byte[4];
- biYPelsPerMeter = new byte[4];
- biClrUsed = new byte[4];
- biClrImportant = new byte[4];
- }
- public byte[] getBiSize() {
- return biSize;
- }
- public void setBiSize(byte[] biSize) {
- this.biSize = biSize;
- }
- public byte[] getBiWidth() {
- return biWidth;
- }
- public void setBiWidth(byte[] biWidth) {
- this.biWidth = biWidth;
- }
- public byte[] getBiHeight() {
- return biHeight;
- }
- public void setBiHeight(byte[] biHeight) {
- this.biHeight = biHeight;
- }
- public byte[] getBiPlans() {
- return biPlans;
- }
- public void setBiPlans(byte[] biPlans) {
- this.biPlans = biPlans;
- }
- public byte[] getBiBitCount() {
- return biBitCount;
- }
- public void setBiBitCount(byte[] biBitCount) {
- this.biBitCount = biBitCount;
- }
- public byte[] getBiCompression() {
- return biCompression;
- }
- public void setBiCompression(byte[] biCompression) {
- this.biCompression = biCompression;
- }
- public byte[] getBiSizeImage() {
- return biSizeImage;
- }
- public void setBiSizeImage(byte[] biSizeImage) {
- this.biSizeImage = biSizeImage;
- }
- public byte[] getBiXPelsPerMeter() {
- return biXPelsPerMeter;
- }
- public void setBiXPelsPerMeter(byte[] biXPelsPerMeter) {
- this.biXPelsPerMeter = biXPelsPerMeter;
- }
- public byte[] getBiYPelsPerMeter() {
- return biYPelsPerMeter;
- }
- public void setBiYPelsPerMeter(byte[] biYPelsPerMeter) {
- this.biYPelsPerMeter = biYPelsPerMeter;
- }
- public byte[] getBiClrUsed() {
- return biClrUsed;
- }
- public void setBiClrUsed(byte[] biClrUsed) {
- this.biClrUsed = biClrUsed;
- }
- public byte[] getBiClrImportant() {
- return biClrImportant;
- }
- public void setBiClrImportant(byte[] biClrImportant) {
- this.biClrImportant = biClrImportant;
- }
- }
- package com.pan.entity;
- /**
- * @author yp2
- * @date 2015-11-17
- * @description Bmp调色板
- */
- public class BmpPalette {
- private byte[][] palettes; //颜色索引映射表
- public byte[][] getPalettes() {
- return palettes;
- }
- public void setPalettes(byte[][] palettes) {
- this.palettes = palettes;
- }
- }
- package com.pan.utils;
- /**
- * @author yp2
- * @date 2015-11-18
- * @description 字节操作工具
- */
- public class ByteUtil {
- /**
- * 将byte数组转换为16进制字符串
- * <br/>
- * 实现思路:
- * 先将byte转换成int,再使用Integer.toHexString(int)
- * @param data byte数组
- * @return
- */
- public static String byteToHex(byte[] data, int start, int end) {
- StringBuilder builder = new StringBuilder();
- for(int i = start; i < end; i++) {
- int tmp = data[i] & 0xff;
- String hv = Integer.toHexString(tmp);
- if(hv.length() < 2) {
- builder.append("0");
- }
- builder.append(hv);
- /*builder.append(" ");*/
- if(i % 16 == 15) {
- /*builder.append("\n");*/
- }
- }
- return builder.toString();
- }
- /**
- * 将byte数组转换为16进制字符串(该字符串方便查看)
- * 输出信息版:16个字节一行显示
- * @param data
- * @param start
- * @param end
- * @return
- */
- public static String byteToHexforPrint(byte[] data, int start, int end) {
- StringBuilder builder = new StringBuilder();
- for(int i = start; i < end; i++) {
- int tmp = data[i] & 0xff;
- String hv = Integer.toHexString(tmp);
- if(hv.length() < 2) {
- builder.append("0");
- }
- builder.append(hv);
- builder.append(" ");
- if(i % 16 == 15) {
- builder.append("\n");
- }
- }
- return builder.toString();
- }
- /**
- * 十六进制字符串转换为字节数组
- * @param hexStr 十六进制字符串
- * @return 字节数组
- */
- public static byte[] hexToByte(String hexStr) {
- byte[] datas = new byte[(hexStr.length() - 1) / 2 + 1];
- hexStr = hexStr.toUpperCase();
- int pos = 0;
- for(int i = 0; i < hexStr.length(); i+=2) {
- if(i + 1 < hexStr.length()) {
- datas[pos] = (byte) ((indexOf(hexStr.charAt(i)+"") << 4) + indexOf(hexStr.charAt(i+1)+""));
- }
- pos++;
- }
- return datas;
- }
- /**
- * 计算指定字符串(这里要求是字符)的16进制所表示的数字
- * @param str
- * @return
- */
- public static int indexOf(String str) {
- return "0123456789ABCDEF".indexOf(str);
- }
- /**
- * 计算byte数组所表示的值,字节数组的值以小端表示,低位在低索引上,高位在高索引
- * <br/>
- * 例:data = {1,2},那么结果为: 2 << 8 + 1 = 513
- * @param data byte数组
- * @return 计算出的值
- */
- public static long lowByteToLong(byte[] data) {
- long sum = 0;
- for(int i = 0; i < data.length; i++) {
- long value = ((data[i] & 0xff) << (8 * i));
- sum += value;
- }
- return sum;
- }
- /**
- * 计算byte数组所表示的值,字节数组的值以大端表示,低位在高索引上,高位在低索引
- * <br/>
- * 例:data = {1,2},那么结果为: 1 << 8 + 2 = 258
- * @param data byte数组
- * @return 计算出的值
- */
- public static long highByteToLong(byte[] data) {
- long sum = 0;
- for(int i = 0; i < data.length; i++) {
- long value = ((data[i] & 0xff) << (8 * (data.length - i - 1)));
- sum += value;
- }
- return sum;
- }
- /**
- * 计算byte数组所表示的值,字节数组的值以小端表示,低位在低索引上,高位在高索引
- * <br/>
- * 例:data = {1,2},那么结果为: 2 << 8 + 1 = 513
- * @param data byte数组
- * @return 计算出的值
- */
- public static int lowByteToInt(byte[] data) {
- int sum = 0;
- for(int i = 0; i < data.length; i++) {
- long value = ((data[i] & 0xff) << (8 * i));
- sum += value;
- }
- return sum;
- }
- /**
- * 计算byte数组所表示的值,字节数组的值以大端表示,低位在高索引上,高位在低索引
- * <br/>
- * 例:data = {1,2},那么结果为: 1 << 8 + 2 = 258
- * @param data byte数组
- * @return 计算出的值
- */
- public static int highByteToInt(byte[] data) {
- int sum = 0;
- for(int i = 0; i < data.length; i++) {
- long value = ((data[i] & 0xff) << (8 * (data.length - i - 1)));
- sum += value;
- }
- return sum;
- }
- /**
- * long值转换为指定长度的小端字节数组
- * @param data long值
- * @param len 长度
- * @return 字节数组,小端形式展示
- */
- public static byte[] longToLowByte(long data, int len) {
- byte[] value = new byte[len];
- for(int i = 0; i < len; i++) {
- value[i] = (byte) ((data >> (8 * i )) & 0xff);
- }
- return value;
- }
- /**
- * long值转换为指定长度的大端字节数组
- * @param data long值
- * @param len 长度
- * @return 字节数组,大端形式展示
- */
- public static byte[] longToHighByte(long data, int len) {
- byte[] value = new byte[len];
- for(int i = 0; i < len; i++) {
- value[i] = (byte) ((data >> (8 * (len - 1 - i) )) & 0xff);
- }
- return value;
- }
- /**
- * int值转换为指定长度的小端字节数组
- * @param data int值
- * @param len 长度
- * @return 字节数组,小端形式展示
- */
- public static byte[] intToLowByte(int data, int len) {
- byte[] value = new byte[len];
- for(int i = 0; i < len; i++) {
- value[i] = (byte) ((data >> (8 * i )) & 0xff);
- }
- return value;
- }
- /**
- * int值转换为指定长度的大端字节数组
- * @param data int值
- * @param len 长度
- * @return 字节数组,大端形式展示
- */
- public static byte[] intToHighByte(int data, int len) {
- byte[] value = new byte[len];
- for(int i = 0; i < len; i++) {
- value[i] = (byte) ((data >> (8 * (len - 1 - i) )) & 0xff);
- }
- return value;
- }
- /**
- * 计算base的exponent次方
- * @param base 基数
- * @param exponent 指数
- * @return
- */
- public static long power(int base, int exponent) {
- long sum = 1;
- for(int i = 0; i < exponent; i++) {
- sum *= base;
- }
- return sum;
- }
- public static void main(String[] args) {
- byte[] data = new byte[]{1,2};
- System.out.println(highByteToInt(data));
- System.out.println(lowByteToInt(data));
- System.out.println(byteToHex(intToHighByte(258, 4), 0, 4));
- System.out.println(byteToHex(intToLowByte(258, 4), 0, 4));
- }
- }
- package com.pan.utils;
- import java.io.BufferedInputStream;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import com.pan.entity.Bmp;
- import com.pan.entity.BmpHeader;
- import com.pan.entity.BmpInfoHeader;
- import com.pan.entity.BmpPalette;
- /**
- * @author yp2
- * @date 2015-11-18
- * @description 重构后的Bmp工具
- * <br/>
- * 主要做了几件事: <br/>
- * 1.位图数据可以不存储,在需要写入的时候再去读原文件的位图数据部分<br/>
- * 2.抽除掉重复功能的代码<br/>
- */
- public class BmpUtilRefactoring {
- /**
- * 读取指定bmp文件的信息到对象中
- * @param bmpFile bmp文件路径
- * @return 代表Bmp文件信息的对象
- */
- private static Bmp readBmp(String bmpFile) {
- Bmp bmp = new Bmp();
- File file = new File(bmpFile);
- InputStream in = null;
- try {
- in = new BufferedInputStream(new FileInputStream(file));
- in.mark(0);
- readBmpHeader(bmp, in);
- long bfOffBits = ByteUtil.lowByteToLong(bmp.getBmpHeader().getBfOffBits());
- long biSize = ByteUtil.lowByteToLong(bmp.getBmpInfoHeader().getBiSize());
- long biBitCount = ByteUtil.lowByteToLong(bmp.getBmpInfoHeader().getBiBitCount());
- int index = (int) (14 + biSize);
- //重新定位到调色板
- in.reset();
- in.skip(index);
- if(bfOffBits - biSize - 14 == 0) {
- //没有调色板
- System.out.println(ByteUtil.lowByteToLong(bmp.getBmpInfoHeader().getBiBitCount()) + "位色无调色板");
- } else {
- //有调色板
- byte[][] palettes = new byte[(int) ByteUtil.power(2, (int) biBitCount)][4];
- for(int i = 0; i < palettes.length && index < bfOffBits; i++) {
- in.read(palettes[i], 0, palettes[i].length);
- index += palettes[i].length;
- }
- BmpPalette bmpPalette = new BmpPalette();
- bmpPalette.setPalettes(palettes);
- bmp.setBmpPalette(bmpPalette);
- }
- //记录bmp文件位图数据
- /*
- int len = -1;
- byte[] buf = new byte[1024];
- StringBuilder data = new StringBuilder();
- while((len = in.read(buf, 0, buf.length)) > 0) {
- data.append(ByteUtil.byteToHex(buf,0, len));
- }
- bmp.setDatas(ByteUtil.hexToByte(data.toString()));*/
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- in.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- return bmp;
- }
- /**
- * 读取bmp文件输入流的头部信息到Bmp中的头部信息中,要求输入流处于文件的开头
- * @param bmp Bmp对象
- * @param in bmp文件输入流
- * @throws IOException
- */
- private static void readBmpHeader(Bmp bmp, InputStream in) throws IOException {
- BmpHeader bmpHeader = new BmpHeader();
- in.read(bmpHeader.getBfType(), 0, bmpHeader.getBfType().length);
- in.read(bmpHeader.getBfSize(), 0, bmpHeader.getBfSize().length);
- in.read(bmpHeader.getBfReserved1(), 0, bmpHeader.getBfReserved1().length);
- in.read(bmpHeader.getBfReserved2(), 0, bmpHeader.getBfReserved2().length);
- in.read(bmpHeader.getBfOffBits(), 0, bmpHeader.getBfOffBits().length);
- bmp.setBmpHeader(bmpHeader);
- BmpInfoHeader bmpInfoHeader = new BmpInfoHeader();
- in.read(bmpInfoHeader.getBiSize(), 0, bmpInfoHeader.getBiSize().length);
- in.read(bmpInfoHeader.getBiWidth(), 0, bmpInfoHeader.getBiWidth().length);
- in.read(bmpInfoHeader.getBiHeight(), 0, bmpInfoHeader.getBiHeight().length);
- in.read(bmpInfoHeader.getBiPlans(), 0, bmpInfoHeader.getBiPlans().length);
- in.read(bmpInfoHeader.getBiBitCount(), 0, bmpInfoHeader.getBiBitCount().length);
- in.read(bmpInfoHeader.getBiCompression(), 0, bmpInfoHeader.getBiCompression().length);
- in.read(bmpInfoHeader.getBiSizeImage(), 0, bmpInfoHeader.getBiSizeImage().length);
- in.read(bmpInfoHeader.getBiXPelsPerMeter(), 0, bmpInfoHeader.getBiXPelsPerMeter().length);
- in.read(bmpInfoHeader.getBiYPelsPerMeter(), 0, bmpInfoHeader.getBiYPelsPerMeter().length);
- in.read(bmpInfoHeader.getBiClrUsed(), 0, bmpInfoHeader.getBiClrUsed().length);
- in.read(bmpInfoHeader.getBiClrImportant(), 0, bmpInfoHeader.getBiClrImportant().length);
- bmp.setBmpInfoHeader(bmpInfoHeader);
- }
- /**
- * 写入要隐藏文件的内容和原Bmp文件信息到指定数据文件中
- * @param bmp 原Bmp文件信息
- * @param inputFileName 要隐藏的文件
- * @param outFileName 输出的文件
- * @throws IOException
- */
- private static void writeFileToBmp(Bmp bmp, String bmpFileName, String inputFileName, String outFileName) throws IOException {
- File inputFile = new File(inputFileName);
- File outFile = new File(outFileName);
- File bmpFile = new File(bmpFileName);
- if(!outFile.exists()) {
- outFile.createNewFile();
- }
- //记录原来bmp文件的数据偏移位置
- long oldbfOffBits = ByteUtil.lowByteToLong(bmp.getBmpHeader().getBfOffBits());
- //计算出新的数据偏移位置:= 原来的偏移位置 + 要隐藏文件的总字节数
- long bfOffBits = inputFile.length() + ByteUtil.lowByteToLong(bmp.getBmpHeader().getBfOffBits());
- //设置新的数据偏移位置,以便写入新的文件中
- bmp.getBmpHeader().setBfOffBits(ByteUtil.longToLowByte(bfOffBits, 4));
- InputStream in = null;
- InputStream bmpIn = null;
- OutputStream out = null;
- try {
- in = new FileInputStream(inputFile);
- bmpIn = new BufferedInputStream(new FileInputStream(bmpFile));
- out = new FileOutputStream(outFile);
- //将bmp头部信息写入输入流中
- writeBmpHeader(bmp, out);
- //写入要隐藏的文件内容
- int len = -1;
- byte[] buf = new byte[1024];
- while((len = in.read(buf)) > 0) {
- out.write(buf, 0, len);
- }
- //跳过头部和调色板信息
- bmpIn.skip(oldbfOffBits);
- len = -1;
- //写入原有位图数据
- while((len = bmpIn.read(buf)) > 0) {
- out.write(buf, 0, len);
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- try {
- in.close();
- out.close();
- bmpIn.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- /**
- * 将文件内容写入到指定的位图文件内,并改变输出文件名
- * @param bmpFileName 位图文件名
- * @param inputFileName 要隐藏的文件名
- * @param outFileName 输出文件名
- * @throws IOException
- */
- public static void writeFileToBmpFile(String bmpFileName, String inputFileName, String outFileName) throws IOException {
- Bmp bmp = readBmp(bmpFileName);
- writeFileToBmp(bmp, bmpFileName, inputFileName, outFileName);
- }
- /**
- * 读取bmp文件中隐藏的文件内容到指定的输出文件中去
- * @param bmpFileName bmp文件名
- * @param outFileName 输出文件名
- * @throws IOException
- */
- public static void readFileFromBmpFile(String bmpFileName, String outFileName) throws IOException {
- File bmpFile = new File(bmpFileName);
- File outFile = new File(outFileName);
- Bmp bmp = new Bmp();
- if(!outFile.exists()) {
- outFile.createNewFile();
- }
- InputStream in = null;
- OutputStream out = null;
- int len = -1;
- try {
- in = new BufferedInputStream(new FileInputStream(bmpFile));
- out = new FileOutputStream(outFile);
- //标记当前输入流位置,方便后面reset跳转到当前输入流读取位置
- in.mark(0);
- //读取输入流中包含的头部信息
- readBmpHeader(bmp, in);
- //数据偏移位置
- long bfOffBits = ByteUtil.lowByteToLong(bmp.getBmpHeader().getBfOffBits());
- //使用的颜色索引数目
- long biClrUsed = ByteUtil.lowByteToLong(bmp.getBmpInfoHeader().getBiClrUsed());
- //位图信息头部字节数
- long biSize = ByteUtil.lowByteToLong(bmp.getBmpInfoHeader().getBiSize());
- //比特/像素
- long biBitCount = ByteUtil.lowByteToLong(bmp.getBmpInfoHeader().getBiBitCount());
- //重置到mark标记的位置,这里是跳转到输入流的开头
- in.reset();
- //保存当前文件输入流位置(字节位置)
- long sumLen = 0;
- if(biBitCount < 24) {
- if(biClrUsed == 0) {
- //索引全部都重要
- //跳过输入流中的54 + 调色板所占字节数 个字节,这样其实就跳转到了保存隐藏文件内容的位置
- in.skip(14 + biSize + ByteUtil.power(2, (int) biBitCount) * 4);
- sumLen = 14 + biSize + ByteUtil.power(2, (int) biBitCount) * 4;
- } else {
- //部分重要
- in.skip(14 + biSize + biClrUsed * 4);
- sumLen = 14 + biSize + biClrUsed * 4;
- }
- } else {
- //没有调色板
- in.skip(14 + biSize);
- sumLen = 14 + biSize;
- }
- byte[] buf = new byte[1024];
- while((len = in.read(buf)) > 0) {
- if((sumLen + len) > bfOffBits) {
- //如果超过了数据偏移位置,则截取剩余的字节进行保存
- out.write(buf, 0, (int) (bfOffBits - sumLen));
- break;
- } else {
- //没有超过数据偏移位置,则截取读取到的字节
- out.write(buf, 0, len);
- }
- sumLen += len;
- }
- } catch (Exception e) {
- e.printStackTrace();
- in.close();
- out.close();
- }
- }
- /**
- * 将bmp头部信息和调色板信息写入输入流中
- * @param out
- * @param bmp
- * @throws IOException
- */
- private static void writeBmpHeader(Bmp bmp, OutputStream out) throws IOException {
- BmpHeader bmpHeader = bmp.getBmpHeader();
- out.write(bmpHeader.getBfType());
- out.write(bmpHeader.getBfSize());
- out.write(bmpHeader.getBfReserved1());
- out.write(bmpHeader.getBfReserved2());
- out.write(bmpHeader.getBfOffBits());
- BmpInfoHeader bmpInfoHeader = bmp.getBmpInfoHeader();
- out.write(bmpInfoHeader.getBiSize());
- out.write(bmpInfoHeader.getBiWidth());
- out.write(bmpInfoHeader.getBiHeight());
- out.write(bmpInfoHeader.getBiPlans());
- out.write(bmpInfoHeader.getBiBitCount());
- out.write(bmpInfoHeader.getBiCompression());
- out.write(bmpInfoHeader.getBiSizeImage());
- out.write(bmpInfoHeader.getBiXPelsPerMeter());
- out.write(bmpInfoHeader.getBiYPelsPerMeter());
- out.write(bmpInfoHeader.getBiClrUsed());
- out.write(bmpInfoHeader.getBiClrImportant());
- BmpPalette bmpPalette = bmp.getBmpPalette();
- if(bmpPalette != null && bmpPalette.getPalettes() != null) {
- for(int i = 0; i < bmpPalette.getPalettes().length; i++) {
- out.write(bmpPalette.getPalettes()[i]);
- }
- }
- }
- }
- package com.pan.main;
- import java.io.IOException;
- import com.pan.utils.BmpUtilRefactoring;
- public class Main {
- public static void main(String[] args) throws IOException {
- /*1位色*/
- BmpUtilRefactoring.writeFileToBmpFile(Main.class.getClassLoader().getResource("resource/SmallConfetti.bmp").getPath(),
- Main.class.getClassLoader().getResource("resource/SmallConfettiscrect.txt").getPath(),
- Main.class.getClassLoader().getResource("resource/").getPath() + "SmallConfettiout.bmp");
- BmpUtilRefactoring.readFileFromBmpFile(Main.class.getClassLoader().getResource("resource/").getPath() + "SmallConfettiout.bmp",
- Main.class.getClassLoader().getResource("resource/").getPath() + "SmallConfettiscrectout.txt");
- /*4位色*/
- BmpUtilRefactoring.writeFileToBmpFile(Main.class.getClassLoader().getResource("resource/verisign.bmp").getPath(),
- Main.class.getClassLoader().getResource("resource/verisignscrect.txt").getPath(),
- Main.class.getClassLoader().getResource("resource/").getPath() + "verisignout.bmp");
- BmpUtilRefactoring.readFileFromBmpFile(Main.class.getClassLoader().getResource("resource/").getPath() + "verisignout.bmp",
- Main.class.getClassLoader().getResource("resource/").getPath() + "verisignscrectout.txt");
- BmpUtilRefactoring.writeFileToBmpFile(Main.class.getClassLoader().getResource("resource/srun.bmp").getPath(),
- Main.class.getClassLoader().getResource("resource/srunscrect.txt").getPath(),
- Main.class.getClassLoader().getResource("resource/").getPath() + "srunout.bmp");
- BmpUtilRefactoring.readFileFromBmpFile(Main.class.getClassLoader().getResource("resource/").getPath() + "srunout.bmp",
- Main.class.getClassLoader().getResource("resource/").getPath() + "srunscrectout.txt");
- /*8位色*/
- BmpUtilRefactoring.writeFileToBmpFile(Main.class.getClassLoader().getResource("resource/SplashScreen.bmp").getPath(),
- Main.class.getClassLoader().getResource("resource/SplashScreenscrect.txt").getPath(),
- Main.class.getClassLoader().getResource("resource/").getPath() + "SplashScreenout.bmp");
- BmpUtilRefactoring.readFileFromBmpFile(Main.class.getClassLoader().getResource("resource/").getPath() + "SplashScreenout.bmp",
- Main.class.getClassLoader().getResource("resource/").getPath() + "SplashScreenscrectout.txt");
- /*24位色*/
- BmpUtilRefactoring.writeFileToBmpFile(Main.class.getClassLoader().getResource("resource/background.bmp").getPath(),
- Main.class.getClassLoader().getResource("resource/backgroundscrect.txt").getPath(),
- Main.class.getClassLoader().getResource("resource/").getPath() + "backgroundout.bmp");
- BmpUtilRefactoring.readFileFromBmpFile(Main.class.getClassLoader().getResource("resource/").getPath() + "backgroundout.bmp",
- Main.class.getClassLoader().getResource("resource/").getPath() + "backgroundscrectout.txt");
- /*32位色*/
- BmpUtilRefactoring.writeFileToBmpFile(Main.class.getClassLoader().getResource("resource/WindowsMail.bmp").getPath(),
- Main.class.getClassLoader().getResource("resource/WindowsMailscrect.txt").getPath(),
- Main.class.getClassLoader().getResource("resource/").getPath() + "WindowsMailout.bmp");
- BmpUtilRefactoring.readFileFromBmpFile(Main.class.getClassLoader().getResource("resource/").getPath() + "WindowsMailout.bmp",
- Main.class.getClassLoader().getResource("resource/").getPath() + "WindowsMailscrectout.txt");
- }
- }
代码下载:搓http://download.csdn.net/detail/u012009613/9280153
转载请注明出处,谢谢!