怎么通过序列化将输入流写入文件或通过socket传输
众所周知,java.io.InputStream是不可序列化的,但是如何序列化一个带有InputStream的类呢?可以通过将流转换成字节数组来实现,这里利用序列化的机制来实现。
在java.io.Serializable这个标记接口的API中有这样的描述:
在序列化和反序列化过程中需要特殊处理的类必须使用下列准确签名来实现特殊方法:
private void writeObject(java.io.ObjectOutputStream out) throws IOException private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;
writeObject 方法负责写入特定类的对象的状态,以便相应的 readObject 方法可以还原它。通过调用 out.defaultWriteObject 可以调用保存 Object 的字段的默认机制。该方法本身不需要涉及属于其超类或子类的状态。状态是通过使用 writeObject 方法或使用 DataOutput 支持的用于基本数据类型的方法将各个字段写入 ObjectOutputStream 来保存的。
下面就用这个机制来实现:
import java.io.*; public class Test { public static void main(String... arguments) throws Exception { if(arguments.length != 1) { System.out.println("Usage:java Test [w|r]"); return; } String option = arguments[0]; if("w".equalsIgnoreCase(option)) { FileOutputStream fos = null; //序列化后的文件 ObjectOutputStream oos = null; //序列化后的文件 FileInputStream fis = null; //读取的文件流 try { fos = new FileOutputStream("a.dat"); oos = new ObjectOutputStream(fos); fis = new FileInputStream("刘雁 - 断桥伞.mp3"); Document doc = new Document("断桥伞.mp3", fis); oos.writeObject(doc); } finally { if(oos != null) { oos.close(); } if(fos != null) { fos.close(); } if(fis != null) { fis.close(); } } } else if("r".equalsIgnoreCase(option)) { FileInputStream fis = null; ObjectInputStream ois = null; try { fis = new FileInputStream("a.dat"); ois = new ObjectInputStream(fis); Document doc = (Document) ois.readObject(); doc.saveFile(); } finally { if(ois != null) { ois.close(); } if(fis != null) { fis.close(); } } } else { System.out.println("Usage:java Test [w|r]"); } } } class Document implements Serializable { private String fileName; //InputStream 不能被序列化 private transient InputStream inputStream; public Document(String fileName, InputStream inputStream) { if(fileName == null || fileName.trim().length() == 0 || inputStream == null) { throw new IllegalArgumentException("fileName:" + fileName + " | inputStream:" + inputStream); } this.fileName = fileName; this.inputStream = inputStream; } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { System.out.println("调用了ReadObject"); ois.defaultReadObject();//读取可反序列化的内容 //读取流 ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int ret = -1; while((ret = ois.read(buffer, 0, 1024)) != -1) { baos.write(buffer, 0, ret); } byte[] data = baos.toByteArray(); inputStream = new ByteArrayInputStream(data); } private void writeObject(ObjectOutputStream oos) throws IOException { System.out.println("调用了writeObject"); oos.defaultWriteObject();//将可以序列化的内容写出到流 //接下来将inputStream也写出 byte[] buffer = new byte[1024]; int ret = -1; while((ret = inputStream.read(buffer, 0, 1024)) != -1) { oos.write(buffer, 0, ret); } oos.flush(); } public void saveFile() throws IOException, FileNotFoundException { FileOutputStream fos = null; try { fos = new FileOutputStream(fileName); byte[] buffer = new byte[1024]; int ret = -1; while((ret = inputStream.read(buffer, 0, 1024)) != -1) { fos.write(buffer, 0, ret); } } finally { if(fos != null) { fos.close(); } } } }
在Test.class文件的同一目录下放入一个mp3文件,我用的是:刘雁 - 断桥伞.mp3
Document类是一个可序列化类,包括两个字段,输入流和一个文件名fileName,可以通过调用该类对象的saveFile方法将该输入流中的数据保存以fileName为名称的文件。
执行java Test w的时候,将一个Document类实例序列化后写入文件,包括“刘雁 - 断桥伞.mp3”的数据。
执行java Test r,将从之前写入的序列化文件中读取Document对象,然后调用其上的saveFile方法,保存数据。
通常,我们很少需要在Serializable类中添加readObject和writeObject方法,这里为了写入inputStream字段的数据,添加了这两个方法,这两个方法的签名必须同上述描述中的一样,否则不会在序列化或反序列化对象时调用,所以在测试时,总是打印一句话以确保这两个方法确实在序列化或反序列化时调用了
将inputStream的数据写入序列化文件很简单,从inputStream读取字节,然后写入objectOutputStream。
从序列化文件读取数据亦然,在读取了可序列化字段后,然后从objectInputStream中读取字节。