将文件内容隐藏在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
转载请注明出处,谢谢!
将文件内容隐藏在bmp位图中的更多相关文章
- C.C++把整个文件内容读进一个buffer中
原创文章,未经本人允许禁止转载. //C方式, 调用的函数繁多 //fopen,fseek,ftell,fseek,malloc,fread,fclose,free. void foo() { FIL ...
- 五种方式让你在java中读取properties文件内容不再是难题
一.背景 最近,在项目开发的过程中,遇到需要在properties文件中定义一些自定义的变量,以供java程序动态的读取,修改变量,不再需要修改代码的问题.就借此机会把Spring+SpringMVC ...
- PHP读取文件内容的五种方式(转载)
php读取文件内容的五种方式 分享下php读取文件内容的五种方法:好吧,写完后发现文件全部没有关闭.实际应用当中,请注意关闭 fclose($fp); php读取文件内容: -----第一种方法--- ...
- Java&Xml教程(七)使用JDOM修改XML文件内容
JDOM提供了非常灵活的方式操作XML文件,使用JDOM非常简单而且代码简洁可读性强.前面我们学习了如何使用JDOM解析XML文件,本节介绍如何使用JDOM修改XML文件内容. 在这个教程中,我们准备 ...
- .NET CORE下最快比较两个文件内容是否相同的方法
本文因为未考虑磁盘缓存, 结果不是很准确, 更严谨的结果请参看本博文的续集 最近项目有个需求,需要比较两个任意大小文件的内容是否相同,要求如下: 项目是.NET CORE,所以使用C#进行编写比较方法 ...
- linux函数深入探索——open函数打开文件是否将文件内容加载到内存空间
转自:https://blog.csdn.net/qq_17019203/article/details/85051627 问题:open(2)函数打开文件是否将文件内容加载到内存空间 首先,文件打开 ...
- PHP读取文件内容的五种方式
-----第一种方法-----fread()-------- <?php $file_path = "test.txt"; if(file_exists($file_path ...
- php 写入文件 读取文件内容
1.写入文件 fopen("文件名.扩展名","操作方式") fwrite(读取的文件,"写入的文件"); fclose(打开的对象变量); ...
- 如何将打印内容转换为bmp位图文件
bmp是一种与硬件设备无关的图像文件格式,使用非常广.它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BblP文件所占用的空间很大.BMP文件的图像深度可选lbit.4bit.8 ...
随机推荐
- MySQL数据查询之单表查询
单表查询 简单查询 - 创建表 DROP TABLE IF EXISTS `person`; CREATE TABLE `person` ( `id` ) NOT NULL AUTO_INCREMEN ...
- python基础之Day6
一.元组 定义:t=(1,2,3,4) 总结:存多个值,值为任意类型 只有读的需求,没有改的需求 有序,不可变(元组里每个值对应的索引内存地址不能变) 在元素个数相同的情况下,元组比列表更节省空间 二 ...
- python 实践项目
项目一:让用户输入圆的半径,告诉用户圆的面积 思路: 1.首先需要让用户输入一个字符串,即圆的半径 2.判断用户输入的字符串是否为数字 isalpha 3.求圆的面积需要调用到math模块,所以要导 ...
- (PMP)第8章-----项目质量管理
过程质量管理,成果质量的管理 戴明理论:PDCA,戴明环 朱兰理论:质量规划,质量控制,质量改进,朱兰三部曲 克鲁斯比理论:零缺陷,质量免费 石川理论:质量圈,因果图,质量管理七大工具:核对表,帕累托 ...
- Oracle partition by 使用说明
--用法详解 0.select * from wmg_test; ---测试数据 1.select v1,v2,sum(v2) over(order by v2) as sum --按 ...
- 让IE8支持html5中的video标签
这是一篇综合几个前辈的解决方案. 使用video的时候,要遇到的问题. ①不兼容ie9及其以下版本 在<head>里添加两行, 参考张鑫旭前辈的博客,但是在ie8中薄播放. <!-- ...
- REdis AOF文件结构分析
REdis-4.0之前的AOF文件没有文件头,而从REdis-4.0开始AOF文件带有一个文件头,文件头格式和RDB文件头相同. REdis-4.0版本,如果开启aof-use-rdb-preambl ...
- 2017-2018-1 20155326信息安全系统设计基础》嵌入式C语言课上考试补交
2017-2018-1 20155326信息安全系统设计基础>嵌入式C语言课上考试补交 PPT上的例子 已知位运算规则为: &0 --> 清零 &1 --> 不变 | ...
- Mac 下 Java 多版本切换
Step 1: 安装 jdk1.7 jdk1.8 路径如下: + /Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk + /Library/Java/J ...
- 机器学习随笔01 - k近邻算法
算法名称: k近邻算法 (kNN: k-Nearest Neighbor) 问题提出: 根据已有对象的归类数据,给新对象(事物)归类. 核心思想: 将对象分解为特征,因为对象的特征决定了事对象的分类. ...