欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關(guān)于文件合并與修改md5值的問題

 更新時間:2023年04月08日 16:35:40   作者:CrazyDragon_King  
這篇文章主要介紹了關(guān)于文件合并與修改md5值的問題,使用本博客的方法,不僅僅可以修改md5值,還可以達(dá)到隱藏文件的目的,需要的朋友可以參考下

圖片文件合并

這里并沒有提及關(guān)于如何修改文件的 md5 值的方法,因為合并文件本身就是修改了文件的 md5 值,使用本博客的方法,不僅僅可以修改md5值,還可以達(dá)到隱藏文件的目的。這里強(qiáng)調(diào)一下:之所以能修改md5值,是因為修改了文件的內(nèi)容,并不是程序可以直接修改文件的md5值,文件的md5值是根據(jù)文件本身計算出來的。

想了解 md5 的一些基本知識的可參考下面這篇文章:

如何使用Java計算修改文件的MD5值

前幾天,寫Java爬蟲的時候,偶然間發(fā)現(xiàn)圖像的數(shù)據(jù)重合起來了,然后圖片就產(chǎn)生了問題。(可能也有的沒有問題,那我也看不出來了,哈?。?/p>

因為圖片重合的原因,我就突發(fā)奇想,把很多張圖片數(shù)據(jù)存入一個文件中,這樣做的話,可以用于隱藏某些文件,然后需要的時候再將它們分隔開來 ,這樣似乎很有趣,有想法就要付諸實(shí)踐。所以,就寫了一個demo程序,順便寫一個博客,來記錄一下想法。

文本文件合并

我先來介紹一個簡單的文本文件合并代碼,再來看這個圖片文件合并的,這樣感覺可能會好一點(diǎn),這個代碼是雙十一那天晚上寫的,可以看出來我的代碼風(fēng)格到現(xiàn)在的區(qū)別,也許沒什么變化吧,哈哈!

運(yùn)行效果:
運(yùn)行前:在這個路徑下面有9個文件。

在這里插入圖片描述

運(yùn)行后:產(chǎn)生了一個 merge.txt 文件

在這里插入圖片描述

文件內(nèi)容展示

在這里插入圖片描述

代碼部分
這部分代碼,功能很簡單就是把一個個的文本文件合并后寫入一個總的 merge.txt 文件夾,當(dāng)時學(xué)會了往文件里追加內(nèi)容,所以寫了這個 demo。
簡單來說就是獲取每一個文件(文本文件,我進(jìn)行了過濾。)得到一個輸入流,然后一個循環(huán)內(nèi),每次將一個文件的信息寫入合并的文件內(nèi),循環(huán)結(jié)束,文件合并就完成了。

package com.filemerge;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class FileMerge {
	
	//參數(shù)為一個文件夾路徑
	public static void fileMerge(String path){
		File target = new File(path);    //待合并文件夾目錄
		File output = new File(path+File.separator+"merge.txt");  //合并文件夾位置
		String[] names = target.list((dir,name)->name.contains(".txt"));  //過濾非文本文件,返回值為一個 String 數(shù)組
		BufferedReader reader = null;
		BufferedWriter writer = null;
		//OutputStreamWriter 不要記錯了!
		try {
			writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(output,true)));
			for (String name : names) {
				reader = new BufferedReader(new InputStreamReader(new FileInputStream(target+File.separator+name)));
				String line = null;
				while((line = reader.readLine()) != null) {
					writer.write(line);
					writer.newLine();
				}
				writer.newLine();  //每個文件進(jìn)行換行分隔內(nèi)容!
			}
			System.out.println("File merge successfully!");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				reader.close();
				writer.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

測試代碼:

package com.filemerge;

public class Test {
	public static void main(String[] args) {
		FileMerge.fileMerge("D:/DB/DreamDragon");
	}
}

圖片文件合并(重點(diǎn))

如果看完了上面的文本文件合并的話,不妨再多看一點(diǎn),把下面這個圖片文件的代碼也看了吧,如果有什么錯誤,歡迎指出來。(還有關(guān)于圖片的一些知識,不知道誰能指出來一下。)

代碼如下:合并圖片工具類

package dragon;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

public class ImageMerge {
	//圖片合并路徑,將要合并圖片放入同一個文件夾方便操作
	public static final String mergePath = "D:/DragonDataFile/beauty"; 
	public static final String outputPath = "D:/DragonDataFile/merge";
	
	//工具類,就只是用靜態(tài)方法了,不要創(chuàng)建對象了。
	private ImageMerge() {}
	
	/**執(zhí)行合并操作
	 * 
	 * 思路如下:首先獲取文件夾下面的所有圖片文件信息,
	 * 然后使用輸入輸出流依次將文件進(jìn)行合并操作。
	 * 
	 * 這里的信息是指的文件大小,最重要的是文件的大小,
	 * 考慮其它因素,不記錄文件名,所以拆分時,會丟失文件名,
	 * 但是不影響圖片的顯示。
	 */
	public static void imageMerge() throws IOException {
		File mergeFile = new File(ImageMerge.mergePath);
		File outputFile = new File(ImageMerge.outputPath);
		if (!initPath(mergeFile, outputFile)) {      // 無法創(chuàng)建 mergePath
			throw new FileNotFoundException("無法創(chuàng)建文件夾: "+ImageMerge.mergePath);
		}
		
		try (//創(chuàng)建輸出文件
			BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(outputFile, System.currentTimeMillis()+".jpeg")))){
			File[] files = mergeFile.listFiles();
			recordImageInfo(files, outputFile);   //記錄文件信息,保存于圖片的文件夾下,可能更好點(diǎn)。
			for (File file : files) {				
				try (
					BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))){
					int hasRead = 0;
					byte[] b = new byte[1024];
					while ((hasRead = bis.read(b)) != -1) {
						bos.write(b, 0, hasRead);
					}
				}
			}
		}
	}
	
	//初始化路徑,如果 mergePath 不存在
	private static boolean initPath(File mergeFile, File outputFile) {
		boolean mk_mergeFile = false, mk_outputFile = false;
		if (!mergeFile.exists()) {           // mergePath 不存在
			mk_mergeFile = mergeFile.mkdirs(); 
		} else {
			mk_mergeFile = true;
		}
		if (!outputFile.exists()) {
			mk_outputFile = outputFile.mkdirs();
		} else {
			mk_outputFile = true;
		}
		return mk_mergeFile && mk_outputFile;
	}
	
	//記錄信息
	private static void recordImageInfo(File[] files, File outputFile) throws FileNotFoundException, IOException {
		try (
				BufferedWriter bos = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(outputFile,"mergeImageInfo.txt"), true)))){
			for (File file : files) {     
				String record = file.length()+" ";
				bos.write(record);
				bos.newLine();
			}
		}
	}
	
}

圖片分隔工具類

package dragon;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;

public class ImageSeparate {
	//拆分文件的位置
	private static final String separatePath = "D:/DragonDataFile/separate";
	
	private ImageSeparate() {}
	
	/**
	 * 合并后文件夾下面有兩個文件(應(yīng)該每一批合并文件,一個單獨(dú)的文件夾):
	 * 合并后文件,合并文件信息(大?。?
	 * 
	 * 思路:首先讀取合并文件信息,然后依據(jù)大小依次從文件中取出
	 * 對應(yīng)大小的字節(jié)數(shù),寫入一個文件中。
	 * @throws IOException 
	 * */
	public static void imageSeparate() throws IOException {
		File separateFile = new File(ImageSeparate.separatePath);
		if (initPath(separateFile)) {     //無法創(chuàng)建文件夾
			throw new FileNotFoundException("無法創(chuàng)建文件夾: "+ImageSeparate.separatePath);
		}
		
		File outputFile = new File(ImageMerge.outputPath);
		//下面獲取的都是 String 數(shù)組,但是正常情況下應(yīng)該都是只有一個 String 的字符串
		//獲取圖片文件信息文件
		File[] infoFile = outputFile.listFiles(f->f.getName().contains(".txt"));
		//獲取合并圖片文件
		File[] mergeFile = outputFile.listFiles(f->!f.getName().contains(".txt"));
		// 獲取信息文件信息(圖片的長度)
		List<Long> fileInfo = getFileInfo(infoFile[0]);
		
		try (
			BufferedInputStream bis = new BufferedInputStream(new FileInputStream(mergeFile[0]))){
			fileInfo.stream().forEach(len->{
				String filename = System.currentTimeMillis()+UUID.randomUUID().toString()+".jpeg";
				System.out.println(filename);
				try (
					BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(separateFile, filename)))){
					long record = 0;
					int hasRead = 0;
					byte[] b = new byte[1024];
					/**
					 * 這里處理比較麻煩,我說明一下:
					 * 一次性去讀 len 長度的數(shù)據(jù),考慮到有時候文件會非常大,這個數(shù)據(jù)對內(nèi)存的壓力很大,
					 * 所以舍棄了,如果文件很小,倒也是一個很好的方式(簡便)。
					 * 
					 * 這里采用逐次讀取的方式:(一般圖片都會大于 1024 字節(jié)的,這個不考慮)
					 * 當(dāng)讀取一次后,判斷剩余的字節(jié)數(shù)是否小于 1024,如果小于的話,就直接
					 * 一次性讀取這些字節(jié)數(shù),并寫入文件中,然后跳出循環(huán),本次文件讀取完成。
					 * */
					while ((hasRead = bis.read(b)) != -1) {						
						bos.write(b,0,hasRead);
						//先判斷,再讀取數(shù)據(jù),否則會出錯。
						record += (long)hasRead;
						if (len-record < 1024) {
							long tail = len-record;
							bis.read(new byte[(int)tail]);
							bos.write(b, 0, (int)tail);
							break;
						}
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			});	
		}
		
	}
	
	//獲取信息文件信息(圖片的長度)
	private static List<Long> getFileInfo(File file) throws NumberFormatException, IOException{
		List<Long> fileInfo = new LinkedList<>();
		try (
			BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file)))){
			String line = null;
			while ((line = br.readLine()) != null) {
				//將數(shù)據(jù)轉(zhuǎn)換為 long 再存入集合,或許使用 DataInputStream 更好吧
				//注意,如果這個文件里面被修改了,可能會引發(fā) RuntimeException
				String[] str = line.split(" ");
				fileInfo.add(Long.parseLong(str[0]));
				
				System.out.println(line);
			}
		}
		return fileInfo;
	}
	
	//初始化 拆分文件位置
	private static boolean initPath(File file) {
		return file.mkdirs();
	}
}

測試類

package dragon;

import java.io.IOException;

public class Client {
	public static void main(String[] args) throws IOException, NumberFormatException, ClassNotFoundException {
		//如果需要合并圖片,就使用第一條語句,注釋第二條,
		//如果需要拆分圖片,就使用第二條語句,注釋第一條
			ImageMerge.imageMerge();
		//	ImageSeparate.imageSeparate();
	}
}

說明:
每一個類都含有很多注釋 ,應(yīng)該還是能表達(dá)清楚意思的,有幾點(diǎn)需要說明一下。

運(yùn)行效果:

測試準(zhǔn)備圖片: 注意觀察文件夾的路徑和第一張圖片。

在這里插入圖片描述

測試準(zhǔn)備圖片信息: 注意觀察文件的大小和占用空間信息。

在這里插入圖片描述

合并效果: 注意觀察合并后的圖片和合并文件的路徑。
合并后的文件會產(chǎn)生一個單獨(dú)的文本文件,這里面存儲的是圖片的大小信息,因為恢復(fù)圖片,是需要這些信息的,否則圖片可能就回不來了。

注意:我當(dāng)時看到這個結(jié)果,感覺很奇妙,雖然合并了37張圖片,但是它居然還可以正常顯示第一張圖片的信息,這個可能和圖片本身的存儲形式有關(guān)(我沒有這方面的知識)。

在這里插入圖片描述

文本文件信息截圖:
注:我是以行為單位進(jìn)行存儲的,每行一個數(shù)據(jù),讀取也是這樣的,這樣感覺比較方便。千萬不能修改這個文件的任何信息,否則就無法恢復(fù)圖片的信息了。

在這里插入圖片描述

恢復(fù)圖片: 注意觀察右下角那張圖片,因為我沒有保留文件名,所以生成圖片的文件名是重寫生成的,還有注意到了我的文件名比較長,這個可以參考我開頭的那個博客鏈接,這里是使用當(dāng)前日期的毫秒數(shù)+UUID來生成的圖片名,確實(shí)是比較長了,但是不會重復(fù),這是我需要的。

在這里插入圖片描述

控制臺輸入信息:
我會把讀取的圖片信息(每張圖片的大小數(shù)據(jù))和恢復(fù)圖片時生成的圖片文件名打印出來,這樣調(diào)試比較方便,看起來也很好看,哈!

在這里插入圖片描述

一些細(xì)節(jié)

圖片的合并就是單純的文件合并,只是獲取每一個文件的輸入流,然后將其依次寫入一個輸出流中。(這里使用的是字節(jié)流,圖片可不能使用字符流?。?/strong>

for (File file : files) {				
	try (
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))){
		int hasRead = 0;
		byte[] b = new byte[1024];
		while ((hasRead = bis.read(b)) != -1) {
			bos.write(b, 0, hasRead);
		}
	}
}

這里比較難得是如何從一個整的合并圖片中恢復(fù)所有圖片的信息,因為圖片的特殊存儲格式,如果在圖片的頭部產(chǎn)生錯誤,就無法識別了(我只知道圖片頭部含有一個魔數(shù),用于標(biāo)識圖片,其他的不是很清楚,我沒有這圖像方面的知識,如果有人知道,可以在下面評論。),一個字節(jié)也不行!
我來說一說我的想法:

舉個例子,干巴巴的說著估計很難講的明白。
先看下面這張圖片,假定這是(合并后圖片中)某個圖片 的信息,我們需要在一個完整的輸入流中,完整的取出來這一部分,不能多也不能少! 注意是順序讀取數(shù)據(jù)。再強(qiáng)調(diào)一下,這是中間某一張圖片,也就是這個圖表示某一個圖片的數(shù)據(jù),但是不是整個文件的數(shù)據(jù),也就是說,這個圖片下面還有數(shù)據(jù),最下面那個小于 1024 byte,只是表示這張圖片還剩下少于 1024 byte得數(shù)據(jù)。

所以下面這種讀取方式是錯誤的,無法正確的恢復(fù)圖片。

byte[] b = new byte[1024];
while ((hasRead = bis.read(b)) != -1) {
	bos.write(b, 0, hasRead);
}

在這里插入圖片描述

其實(shí)有一種很簡單的方式,就是下面注釋中的方式,每次直接將整個圖片的數(shù)據(jù)讀取出來,寫入一個輸出流,就是一張完整得圖片了,簡單粗暴,但是我考慮到,有時候圖片太大,對于內(nèi)存是一個很大的消耗,沒有采用這種方式。

仍然采用逐次讀取的方式:
說明:
設(shè)置一個字節(jié)計數(shù)器,在每次讀取(1024byte)之后,下一次讀取之前,判斷當(dāng)前圖片的大小和當(dāng)前讀入的字節(jié)數(shù)的差值是否大于 1024 字節(jié),即是否滿足一次完整的讀取,如果滿足的條件,就繼續(xù)讀取寫入操作,如果不足 1024字節(jié),說明不能再進(jìn)行讀取寫入了(因為當(dāng)前圖片下面還有其它圖片數(shù)據(jù),所以仍然是可以讀取 1024 字節(jié)的,只是屬于當(dāng)前圖片的字節(jié)數(shù),不足 1024 字節(jié)了,即不能進(jìn)行一次完整的讀取了。)所以,如果不足以進(jìn)行一次完整的讀取,那就只讀當(dāng)前還需要的字節(jié),只需要讀取一次就行了,讀取之后將數(shù)據(jù)寫入輸出流,退出當(dāng)前循環(huán),進(jìn)行下一張圖片的讀取。 可以畫圖觀察一下,就會理解了。

	try (
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(separateFile, filename)))){
		long record = 0;
		int hasRead = 0;
		byte[] b = new byte[1024];
		/**
		 * 這里處理比較麻煩,我說明一下:
		 * 一次性去讀 len 長度的數(shù)據(jù),考慮到有時候文件會非常大,這個數(shù)據(jù)對內(nèi)存的壓力很大,
		 * 所以舍棄了,如果文件很小,倒也是一個很好的方式(簡便)。
		 * 
		 * 這里采用逐次讀取的方式:(一般圖片都會大于 1024 字節(jié)的,這個不考慮)
		 * 當(dāng)讀取一次后,判斷剩余的字節(jié)數(shù)是否小于 1024,如果小于的話,就直接
		 * 一次性讀取這些字節(jié)數(shù),并寫入文件中,然后跳出循環(huán),本次文件讀取完成。
		 * */
		while ((hasRead = bis.read(b)) != -1) {						
			bos.write(b,0,hasRead);
			//先判斷,再讀取數(shù)據(jù),否則會出錯。
			record += (long)hasRead;
			if (len-record < 1024) {
				long tail = len-record;
				bis.read(new byte[(int)tail]);
				bos.write(b, 0, (int)tail);
				break;
			}
		}
	} catch (IOException e) {
		e.printStackTrace();
	}

不足之處

如果仔細(xì)閱讀了我的代碼,應(yīng)該可以看出來了,有一些地方寫的不好。
主要有以下幾點(diǎn):
沒有保存圖片的類型,恢復(fù)圖片時,只能強(qiáng)行指定文件的后綴名為 jpeg,這樣做不是很好的做法。
恢復(fù)圖片時,直接指定為jpeg,不太合適。

String filename = System.currentTimeMillis()+UUID.randomUUID().toString()+".jpeg";

這個是創(chuàng)建合并文件時,指定第一張圖片的后綴名,這樣做也不是很好。

new File(outputFile, System.currentTimeMillis()+".jpeg")

所以我對上面代碼進(jìn)行了改進(jìn),在保存圖片的大小信息的同時,保存圖片的后綴名信息(一般都是有的,但是如果沒有的話,我就指定一個 “.none” 作為后綴名了)。一開始我是準(zhǔn)備還是直接按照如下形式存儲:
圖片大小 [空格分隔] 圖片后綴名
但是實(shí)際處理過程中,這樣感覺還是比較麻煩的,因為存儲的數(shù)據(jù)都是字符信息了,Java是沒有辦法直接使用的,顯示轉(zhuǎn)換太麻煩了,所以我決定不使用這種方式了,轉(zhuǎn)而使用Java的對象序列化。因為同時需要大小和后綴名兩個屬性,而且兩個屬性之間也是具有很強(qiáng)關(guān)系的(一對一),干脆封裝一下,做成一個Java類,這樣使用起來很方便,而且兩個屬性之間也建立了聯(lián)系,序列化恢復(fù)也比較方便。而且對象序列化還帶來一個好處,Java的對象序列化是二進(jìn)制序列化,區(qū)別于 json 這種字符序列化,二進(jìn)制是機(jī)器讀取的,我們就算打開了也是亂碼,所以,可以避免這個文件被別人給修改了。(一般是不會去修改二進(jìn)制文件的吧,哈?。?/p>

圖片對象模型

package dragon;

import java.io.Serializable;

/**
 * 文件信息模型類:
 * 記錄文件的大小和后綴名,因為總是
 * 需要使用這個,就把它封裝起來使用吧。
 * */
public class FileInfo implements Serializable{
	/**
	 * 序列化 id
	 */
	private static final long serialVersionUID = 1L;
	private long len;
	private String suffix;
	
	public FileInfo(long len, String suffix) {
		this.len = len;
		this.suffix = suffix;
	}
	
	public long getLen() {
		return this.len;
	}
	
	public String getSuffix() {
		return this.suffix;
	}

    //重寫 toString 方法,方便打印調(diào)試代碼
	@Override
	public String toString() {
		return "FileInfo [len=" + len + ", suffix=" + suffix + "]";
	}
}

對于原來的圖片合并和分隔方法,都進(jìn)行了一點(diǎn)改進(jìn),所以命名規(guī)則上都在原來的類前面加了一個 Enhance (增強(qiáng)、改進(jìn))。

改進(jìn)的圖片合并類:EnhanceImageMerge

package dragon;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.LinkedList;
import java.util.List;

public class EnhanceImageMerge {
	//圖片合并路徑,將要合并圖片放入同一個文件夾方便操作
	public static final String mergePath = "D:/DragonDataFile/beauty"; 
	public static final String outputPath = "D:/DragonDataFile/merge";
	
	//工具類,就只是用靜態(tài)方法了,不要創(chuàng)建對象了。
	private EnhanceImageMerge() {}
	
	/**執(zhí)行合并操作
	 * 
	 * 思路如下:首先獲取文件夾下面的所有圖片文件信息,
	 * 然后使用輸入輸出流依次將文件進(jìn)行合并操作。
	 * 
	 * 這里的信息是指的文件大小,最重要的是文件的大小,
	 * 考慮其它因素,不記錄文件名,所以拆分時,會丟失文件名,
	 * 但是不影響圖片的顯示。
	 */
	public static void imageMerge() throws IOException {
		File mergeFile = new File(EnhanceImageMerge.mergePath);
		File outputFile = new File(EnhanceImageMerge.outputPath);
		if (!initPath(mergeFile, outputFile)) {      // 無法創(chuàng)建 mergePath
			throw new FileNotFoundException("無法創(chuàng)建文件夾: "+EnhanceImageMerge.mergePath);
		}
		
		
		File[] files = mergeFile.listFiles();
		String suffix = recordImageInfo(files, outputFile);   //記錄文件信息,保存于圖片的文件夾下,可能更好點(diǎn)。
		try (//創(chuàng)建輸出文件
			BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(outputFile, System.currentTimeMillis()+suffix)))){
			for (File file : files) {				
				try (
					BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))){
					int hasRead = 0;
					byte[] b = new byte[1024];
					while ((hasRead = bis.read(b)) != -1) {
						bos.write(b, 0, hasRead);
					}
				}
			}
		}
	}
	
	//初始化路徑,如果 mergePath 不存在
	private static boolean initPath(File mergeFile, File outputFile) {
		boolean mk_mergeFile = false, mk_outputFile = false;
		if (!mergeFile.exists()) {           // mergePath 不存在
			mk_mergeFile = mergeFile.mkdirs(); 
		} else {
			mk_mergeFile = true;
		}
		if (!outputFile.exists()) {
			mk_outputFile = outputFile.mkdirs();
		} else {
			mk_outputFile = true;
		}
		return mk_mergeFile && mk_outputFile;
	}
	
	使用對象序列化進(jìn)行數(shù)據(jù)的存儲,方便快捷。
	private static String recordImageInfo(File[] files, File outputFile) throws FileNotFoundException, IOException {
		try (                            //二進(jìn)制保存的數(shù)據(jù),無法直接閱讀,不加擴(kuò)展名了      
			ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(outputFile, "fileinfo"), true))){
			List<FileInfo> fileInfos = new LinkedList<>(); 
			for (File file : files) {
				String filename = file.getName();    //記錄文件的大小和擴(kuò)展名信息 如果沒有的話,默認(rèn)設(shè)置為 none。
				long len = file.length();            
				String suffix = filename.lastIndexOf(".") != -1 ? filename.substring(filename.lastIndexOf(".")) : ".none";
				FileInfo fileInfo = new FileInfo(len, suffix);
				System.out.println(fileInfo.toString());
				fileInfos.add(fileInfo);
			}
			oos.writeObject(fileInfos);   //直接將集合序列化,序列化單個對象,讀取的時候太麻煩了
		}
		String firstFileName = files[0].getName();
		//返回第一個文件的后綴名。
		return firstFileName.lastIndexOf(".") != -1 ? firstFileName.substring(firstFileName.lastIndexOf(".")) : ".none";
	}
}

注意:對象序列化的時候,如果每次序列化一個對象的話,那么讀取的時候,就無法判斷怎么結(jié)束了,因為程序不知道該讀取多少次才結(jié)束,而且似乎不能使用讀取結(jié)果為 null 來判斷,那樣會引發(fā)一個 EOFException。
我去查閱資料,有人推薦了,在序列化的最后,添加一個 null 對象,這確實(shí)是一個很好的方法,但是感覺還是不好。
另一種方式就是直接序列化一個List 集合,這樣確實(shí)是方便多了,存入一個集合,讀取回來了還是一個集合,可以直接操作了,還省去將對象再組裝成集合的時間。(對象序列化,我只是了解,用過那么一兩次,不是很熟。)

對象序列化部分

使用對象序列化進(jìn)行數(shù)據(jù)的存儲,方便快捷。
private static String recordImageInfo(File[] files, File outputFile) throws FileNotFoundException, IOException {
	try (                            //二進(jìn)制保存的數(shù)據(jù),無法直接閱讀,不加擴(kuò)展名了      
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(outputFile, "fileinfo"), true))){
		List<FileInfo> fileInfos = new LinkedList<>(); 
		for (File file : files) {
			String filename = file.getName();    //記錄文件的大小和擴(kuò)展名信息 如果沒有的話,默認(rèn)設(shè)置為 none。
			long len = file.length();            
			String suffix = filename.lastIndexOf(".") != -1 ? filename.substring(filename.lastIndexOf(".")) : ".none";
			FileInfo fileInfo = new FileInfo(len, suffix);
			System.out.println(fileInfo.toString());
			fileInfos.add(fileInfo);
		}
		oos.writeObject(fileInfos);   //直接將集合序列化,序列化單個對象,讀取的時候太麻煩了
	}
	String firstFileName = files[0].getName();
	//返回第一個文件的后綴名。
	return firstFileName.lastIndexOf(".") != -1 ? firstFileName.substring(firstFileName.lastIndexOf(".")) : ".none";
}

改進(jìn)的圖片分隔類:EnhanceImageSeparate

package dragon;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;
import java.util.UUID;

public class EnhanceImageSeparate {
	//拆分文件的位置
	private static final String separatePath = "D:/DragonDataFile/separate";
	
	private EnhanceImageSeparate() {}
	
	/**
	 * 合并后文件夾下面有兩個文件(應(yīng)該每一批合并文件,一個單獨(dú)的文件夾):
	 * 合并后文件,合并文件信息(大?。?。
	 * 
	 * 思路:首先讀取合并文件信息,然后依據(jù)大小依次從文件中取出
	 * 對應(yīng)大小的字節(jié)數(shù),寫入一個文件中。
	 * 
	 * @throws IOException 
	 * @throws ClassNotFoundException 
	 * @throws NumberFormatException 
	 * */
	public static void imageSeparate() throws IOException, NumberFormatException, ClassNotFoundException {
		File separateFile = new File(EnhanceImageSeparate.separatePath);
		if (initPath(separateFile)) {     //無法創(chuàng)建文件夾
			throw new FileNotFoundException("無法創(chuàng)建文件夾: "+EnhanceImageSeparate.separatePath);
		}
		File outputFile = new File(ImageMerge.outputPath);
		//下面獲取的都是 String 數(shù)組,但是正常情況下應(yīng)該都是只有一個 String 的字符串
		//獲取圖片文件信息文件
		File[] infoFile = outputFile.listFiles(f->!f.getName().contains("."));   //序列化文件是沒有后綴名的
		//獲取合并圖片文件
		File[] mergeFile = outputFile.listFiles(f->f.getName().contains(".")); //圖片文件都是有后綴名的
		// 獲取信息文件信息(圖片的長度)
		System.out.println(infoFile[0]);
		List<FileInfo> fileInfos = getFileInfo(infoFile[0]);
		mergeOperation(fileInfos, mergeFile[0], separateFile);
	}
	
	
	/**
	 * 執(zhí)行文件合并操作
	 * @param fileInfos 文件信息集合
	 * @param 需要合并文件的文件夾
	 * @param separateFile 合并操作后的文件夾
	 * 
	 * @throws IOException 
	 * @throws FileNotFoundException 
	 * */
	private static void mergeOperation(List<FileInfo> fileInfos, File mergeFile, File separateFile) throws FileNotFoundException, IOException {
		try (
			BufferedInputStream bis = new BufferedInputStream(new FileInputStream(mergeFile))){
			fileInfos.stream().forEach(fileInfo->{
				long len = fileInfo.getLen();
				String filename = System.currentTimeMillis()+UUID.randomUUID().toString()+fileInfo.getSuffix();
				System.out.println(filename);
				try (
					BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(separateFile, filename)))){
					long record = 0;
					int hasRead = 0;
					byte[] b = new byte[1024];
					/**
					 * 這里處理比較麻煩,我說明一下:
					 * 一次性去讀 len 長度的數(shù)據(jù),考慮到有時候文件會非常大,這個數(shù)據(jù)對內(nèi)存的壓力很大,
					 * 所以舍棄了,如果文件很小,倒也是一個很好的方式(簡便)。
					 * 
					 * 這里采用逐次讀取的方式:(一般圖片都會大于 1024 字節(jié)的,這個不考慮)
					 * 當(dāng)讀取一次后,判斷剩余的字節(jié)數(shù)是否小于 1024,如果小于的話,就直接
					 * 一次性讀取這些字節(jié)數(shù),并寫入文件中,然后跳出循環(huán),本次文件讀取完成。
					 * */
					while ((hasRead = bis.read(b)) != -1) {						
						bos.write(b,0,hasRead);
						//先判斷,再讀取數(shù)據(jù),否則會出錯。
						record += (long)hasRead;
						if (len-record < 1024) {
							long tail = len-record;
							bis.read(new byte[(int)tail]);
							bos.write(b, 0, (int)tail);
							break;
						}
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			});	
		}
	}

	//獲取信息文件信息(圖片的長度)
	//抑制一下 unchecked 警告
	@SuppressWarnings("unchecked")
	private static List<FileInfo> getFileInfo(File file) throws NumberFormatException, IOException, ClassNotFoundException{
		try (
			ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))){
			return (List<FileInfo>) ois.readObject(); //強(qiáng)制類型轉(zhuǎn)換一下,讀取出來的數(shù)據(jù)都是 Object 類型
		}
	}
	
	//初始化 拆分文件位置
	private static boolean initPath(File file) {
		return file.mkdirs();
	}
}

注意: 分隔還原圖片時,圖片的后綴名部分代碼為:
使用Java封裝屬性后,使用很方便了。

String filename = System.currentTimeMillis()+UUID.randomUUID().toString()+fileInfo.getSuffix();

反序列化讀取集合:
這里我抑制了一個強(qiáng)制類型轉(zhuǎn)換的警告。
通過序列化,可以發(fā)現(xiàn)代碼量大大減少了,直接就是集合,使用非常方便。

//抑制一下 unchecked 警告
@SuppressWarnings("unchecked")
private static List<FileInfo> getFileInfo(File file) throws NumberFormatException, IOException, ClassNotFoundException{
	try (
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))){
		return (List<FileInfo>) ois.readObject(); //強(qiáng)制類型轉(zhuǎn)換一下,讀取出來的數(shù)據(jù)都是 Object 類型
	}
}

測試代碼

package dragon;

import java.io.IOException;

public class Client {
	public static void main(String[] args) throws IOException, NumberFormatException, ClassNotFoundException {
		//如果需要合并圖片,就使用第一條語句,注釋第二條,
		//如果需要拆分圖片,就使用第二條語句,注釋第一條
		  EnhanceImageMerge.imageMerge();
	//	 EnhanceImageSeparate.imageSeparate();
	}
}

改進(jìn)后代碼運(yùn)行結(jié)果 執(zhí)行合并方法時,打印的圖片對象模型的信息

在這里插入圖片描述

合并后的效果

注意觀察右邊的 fileinfo 文件,因為是二進(jìn)制數(shù)據(jù),我就沒有給它加上文件后綴名,加上了也是無法直接閱讀的,里面存儲的是圖片對象模型集合的序列化信息。

在這里插入圖片描述

執(zhí)行分隔操作后的效果

控制臺輸出圖片信息,可以看到每個圖片的后綴名都恢復(fù)了,注意看最后一個,有一個文本文件!哈哈!這個圖片后面似乎可以添加任何數(shù)據(jù),也許視頻也是可以的,只是我沒有測試,這個應(yīng)該和圖片的存儲格式、顯示方式有關(guān)。

在這里插入圖片描述

注意,下面恢復(fù)的時候,確實(shí)是有一個文本文件,并且是完好的,可以閱讀的。

在這里插入圖片描述

合并后被分隔出的文本文件的信息

在這里插入圖片描述

總結(jié)

因為一個代碼的bug,而產(chǎn)生的一個奇妙想法,并且付諸實(shí)踐了,收獲了一個挺有趣的代碼,發(fā)現(xiàn)這個還是挺有趣的,這也和我最近在看Java的IO流相關(guān)的部分有關(guān),如果我沒有前期的積累,估計想法只能是想法了(沒有能力實(shí)現(xiàn)自己的各種想法,哈哈!)。寫這個代碼的時候,用到了很多以前的學(xué)過的知識,因為現(xiàn)在沒什么接觸工作的機(jī)會,很多知識不用,逐漸都生疏了,偶爾還是要練練手。新學(xué)的知識 ,那就忘得更快了,很多時候也不是很理解,并且親自實(shí)踐一下才行。
紙上得來終覺淺,絕知此事要躬行。 哈哈。
主要還是記錄自己寫代碼的過程,也算是記錄成長吧。

到此這篇關(guān)于關(guān)于文件合并與修改md5值的問題的文章就介紹到這了,更多相關(guān)文件合并與修改md5值內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • spring使用RedisTemplate的操作類訪問Redis

    spring使用RedisTemplate的操作類訪問Redis

    本篇文章主要介紹了spring使用RedisTemplate的操作類訪問Redis,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • java實(shí)現(xiàn)客戶信息管理系統(tǒng)

    java實(shí)現(xiàn)客戶信息管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)客戶信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-02-02
  • Socket結(jié)合線程池使用實(shí)現(xiàn)客戶端和服務(wù)端通信demo

    Socket結(jié)合線程池使用實(shí)現(xiàn)客戶端和服務(wù)端通信demo

    這篇文章主要為大家介紹了Socket結(jié)合線程池的使用來實(shí)現(xiàn)客戶端和服務(wù)端通信實(shí)戰(zhàn)demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-03-03
  • 詳解Java中int和Integer的區(qū)別

    詳解Java中int和Integer的區(qū)別

    這篇文章主要介紹了Java中int和Integer的區(qū)別文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • Java十分鐘精通異常處理機(jī)制

    Java十分鐘精通異常處理機(jī)制

    異常就是不正常,比如當(dāng)我們身體出現(xiàn)了異常我們會根據(jù)身體情況選擇喝開水、吃藥、看病、等?異常處理方法。?java異常處理機(jī)制是我們java語言使用異常處理機(jī)制為程序提供了錯誤處理的能力,程序出現(xiàn)的錯誤,程序可以安全的退出,以保證程序正常的運(yùn)行等
    2022-03-03
  • Java?Web實(shí)現(xiàn)簡易圖書管理系統(tǒng)

    Java?Web實(shí)現(xiàn)簡易圖書管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了Java?Web實(shí)現(xiàn)簡易圖書管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • Java Lambda List轉(zhuǎn)Map代碼實(shí)例

    Java Lambda List轉(zhuǎn)Map代碼實(shí)例

    這篇文章主要介紹了Java Lambda List轉(zhuǎn)Map代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-03-03
  • Java的Struts框架中<results>標(biāo)簽的使用方法

    Java的Struts框架中<results>標(biāo)簽的使用方法

    這篇文章主要介紹了Java的Struts框架中<results>標(biāo)簽的使用方法,Struts框架是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下
    2015-11-11
  • springboot+EHcache 實(shí)現(xiàn)文章瀏覽量的緩存和超時更新

    springboot+EHcache 實(shí)現(xiàn)文章瀏覽量的緩存和超時更新

    這篇文章主要介紹了springboot+EHcache 實(shí)現(xiàn)文章瀏覽量的緩存和超時更新,問題描述和解決思路給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2017-04-04
  • java全角與半角標(biāo)點(diǎn)符號相互轉(zhuǎn)換詳解

    java全角與半角標(biāo)點(diǎn)符號相互轉(zhuǎn)換詳解

    這篇文章主要為大家介紹了java全角與半角標(biāo)點(diǎn)符號相互轉(zhuǎn)換詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03

最新評論