将文件内容隐藏在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文件的大小,其它什么都不需要获取,数据的隐藏性较差


重要源码:


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

转载请注明出处,谢谢!

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

来了就走下去

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值