1.问题来源

       项目中有个新需求,需要将某个方法从控制台输出的信息抓取后保存

起来保存到数据库表中或者一个文件中并且不能影响原先控制台打印信息的展示。因此基于《Java获取控制台输出信息》对实现方法做了进一步优化,以实现以上需求。


      这里仍然是两个示例,一个用来将控制台信息保存到数据库表中,另外一个则保存到指定的文件中,并且不影响控制台信息的正常打印。代码如下:

2.打印并获取打印信息

控制台可正常打印信息,并且将打印信息抓取出来进行下一步处理

2.1 自定义打印流工具类

import java.io.OutputStream;
import java.io.PrintStream;

/**
 * 继承自 `PrintStream`,可以同时将输出内容发送到两个不同的输出流。
 */
public class TeePrintStream extends PrintStream {
	private PrintStream secondStream; // 第二个输出流

	/**
	 * 构造函数,用于创建 `TeePrintStream` 对象。
	 *
	 * @param mainStream   主要的输出流
	 * @param secondStream 第二个输出流
	 */
	public TeePrintStream(OutputStream mainStream, OutputStream secondStream) {
		super(mainStream); // 调用父类 PrintStream 的构造函数,传入主要的输出流
		this.secondStream = new PrintStream(secondStream); // 创建第二个输出流
	}

	/**
	 * 将字节数组输出到主要和第二个输出流。
	 *
	 * @param buf 输出的字节数组
	 * @param off 数组的起始偏移量
	 * @param len 要写入的字节数
	 */
	@Override
	public void write(byte[] buf, int off, int len) {
		super.write(buf, off, len); // 将字节数组输出到主要输出流
		secondStream.write(buf, off, len); // 将字节数组输出到第二个输出流
	}

	/**
	 * 刷新主要和第二个输出流。
	 */
	@Override
	public void flush() {
		super.flush(); // 刷新主要输出流
		secondStream.flush(); // 刷新第二个输出流
	}

	/**
	 * 关闭第二个输出流。
	 */
	@Override
	public void close() {
		// super.close(); // 务必不要关闭,否则后续正常的标准输出流都无法生效
		secondStream.close(); // 关闭第二个输出流
	}
}

2.2 实现

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.PrintStream;

import com.qfx.modules.test.bean.TeePrintStream;

/**
 * 获取控制台输出信息
 */
/**
 * @author user
 *
 */
public class ConsoleOutputCaptureDemo {
	public static void main(String[] args) {
		ConsoleOutput();
		ConsoleOutputToFile();
		System.out.println("正常输出...");
	}

	/**
	 * 控制台输出的同时,获取控制台输出信息放在最后统一处理
	 */
	public static void ConsoleOutput() {
		// 保存原来的标准输出流
		PrintStream originalOut = new PrintStream(System.out);
		String output = "";
		try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
				TeePrintStream teePrintStream = new TeePrintStream(System.out, baos)) {
			System.setOut(teePrintStream);

			for (int i = 1; i <= 5; i++) {
				System.out.println(i + ": 这条消息会同时在控制台中显示,并在字节数组输出流中存在");
			}
			// 获取捕获到的控制台输出
			output = baos.toString();
		} catch (Exception e) {
			e.getMessage();
		}
        
		// 恢复原始的标准输出流
		System.setOut(originalOut);
        
		System.out.println();
		System.out.println("-------------获取抓取到的控制台信息---------------");
		System.out.println(output);
	}
}

2.3 效果

可以看到正常打印的同时抓取到了控制台打印信息,并重新进行了打印,而且后面的正常输出也没有受到影响

Java获取控制台输出信息(优化版)_输出流

3.打印并保存至文件中

3.1 自定义输出流工具类

import java.io.IOException;
import java.io.OutputStream;

/**
 * 输出流的复制工具,它可以将数据同时写入两个不同的输出流中。
 */
public class TeeOutputStream extends OutputStream {
	private OutputStream mainStream; // 第一个输出流
	private OutputStream outputStream2; // 第二个输出流

	/**
	 * 构造函数,用于创建一个TeeOutputStream对象。
	 *
	 * @param mainStream 第一个输出流
	 * @param outputStream2 第二个输出流
	 */
	public TeeOutputStream(OutputStream mainStream, OutputStream outputStream2) {
		this.mainStream = mainStream;
		this.outputStream2 = outputStream2;
	}

	/**
	 * 将指定的字节写入输出流中。
	 *
	 * @param b 要写入的字节
	 * @throws IOException 如果发生输入/输出异常
	 */
	@Override
	public void write(int b) throws IOException {
		mainStream.write(b);
		outputStream2.write(b);
	}

	/**
	 * 刷新输出流,确保所有缓冲的数据被写入目标流中。
	 *
	 * @throws IOException 如果发生输入/输出异常
	 */
	@Override
	public void flush() throws IOException {
		mainStream.flush();
		outputStream2.flush();
	}

	/**
	 * 关闭输出流,释放任何系统资源。
	 *
	 * @throws IOException 如果发生输入/输出异常
	 */
	@Override
	public void close() throws IOException {
		// mainStream.close();  // 务必不要关闭,否则后续正常的标准输出流都无法生效
		outputStream2.close();
	}
}

3.2 实现

import java.io.FileOutputStream;
import java.io.PrintStream;

import com.qfx.modules.test.bean.TeeOutputStream;

/**
 * 获取控制台输出信息
 */
public class ConsoleOutputCaptureDemo {
	
	public static void main(String[] args) {
		System.out.println();
		ConsoleOutputToFile();
		System.out.println("正常输出...");
	}

	/**
	 * 控制台输出信息的同时输出信息到指定文件中
	 */
	public static void ConsoleOutputToFile() {
		// 保存原来的标准输出流
		PrintStream originalOut = new PrintStream(System.out);
		try (FileOutputStream fileOutputStream = new FileOutputStream("f:/output.txt");
				TeeOutputStream teeOutputStream = new TeeOutputStream(System.out, fileOutputStream);
				PrintStream printStream = new PrintStream(teeOutputStream, true, "UTF-8")) {
			// 将标准输出流重定向到打印流
			System.setOut(printStream);
			
			for (int i = 1; i <= 10; i++) {
				System.out.println(i + ": 这条消息会同时在控制台和文件中显示");
				Thread.sleep(1000);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		// 恢复原始的标准输出流
		System.setOut(originalOut);
	}
}

3.3 效果

3.3.1 效果1

控制台打印信息的同时将信息保存到了指定的文件中

Java获取控制台输出信息(优化版)_java_02

3.3.2 效果2

可以看到,执行完打印并保存至文件中的操作,恢复了标准的输出流,后续的可以正常打印信息

Java获取控制台输出信息(优化版)_输出流_03