我正在尝试通过套接字传输更大的文件。我将把文件分块传输。如代码所示。link
int count;
byte[] buffer = new byte[8192];
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}
在发送这个文件之前,我想发送一个包含文件详细信息的对象。我应该使用哪个流发送Object+File。
我是流媒体的新手,可以获得任何示例代码吗。
我可以先发送对象的字节长度来读取对象、保存它并发送文件数据吗?有可能吗,有样本代码吗?
谢谢421
我不确定使用Java的序列化机制是否是进行简单文件传输的最佳方式。正如你的问题所暗示的那样,你尽量避免在任何时候都把整个文件保存在内存中。这可以使用代理模式对对象执行,但如果您只想传输文件,这可能不是最直接的解决方案。(此外,它还将有效地将您的对等应用程序绑定到Java中。)
相反,为什么不看看一个非常成功的协议,它正是你所需要的:HTTP。
Content-Type: application/octet-stream
Content-Length: 542183
542183 bytes of data follow...
为元数据头编写解析器应该不会太难。
您需要确保写入/读取的顺序。如果客户端上为write an object -> write raw bytes
,则服务器上为read an object -> read raw bytes
。读取时,ObjectInputStream
应该能够找到序列化对象数据的边界。
如果您想使套接字连接长期有效并多次使用其流,那么将套接字的Output/InputStream
封装在ObjectOutput/InputStream
中不是一个好主意。当您关闭对象流时,它也会关闭底层流。
因此,您可能希望首先写入序列化对象数据的长度(文件长度包含在对象中,因此不需要显式写入),例如4字节的BigEndian编码的int
。然后将对象序列化为ByteArrayOutputStream
,并将字节写入其缓冲区。在服务器上,先读取4个字节,将字节解码回int
,然后将这么多字节读取到byte[]
,用ByteArrayInputStream
包裹字节数组,并从中反序列化对象。
这样写:
......
OutputStream out = socket.getOutputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(fileInfoObject);
oos.close();
byte[] header = encodeInteger(baos.size());
out.write(header, 0, 4);
baos.writeTo(out);
// write the file to out just as your question shows
在接收端:。。。。。。InputStream in=socket.getInputStream();
// read the int
byte[] header = new byte[4];
in.read(header, 0, 4);
int size = decodeInteger(header);
// read the object
byte[] objectbuf = new byte[size];
int count;
while((count += in.read(objectbuf)) < size); // not sure if this works...
ObjectInputStram ois = new ObjectInputStream(new ByteArrayInputStream(objectbuf));
Object fileInfoObject = ois.readObject();
ois.close();
// read the file
FileOutputStream fos = new FileOutputStream(new File("somefile"));
byte[] buffer = new byte[8192];
count = 0;
long left = castedFileInfoObject.fileSize;
// also not sure if this works, not tested.
int maxRead = buffer.length;
while (true) {
count = in.read(buffer, 0, maxRead);
left -= count;
if (left < 8192) {
maxRead = (int)left;
}
fos.write(buffer, 0, count);
if (left == 0) {
break;
}
}
我还没有在回答中测试示例代码。。只是为了展示这个想法。
class-MyClass应该实现Serializable接口。然后,可以将此类的对象写入ObjectOutputStream,并使用writeObject和readObject方法从ObjectInputStream中读回(请参见下文)。
客户端:
Socket socket = new Socket(url, port);
OutputStream os = socket.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
MyClass obj = new Myclass();
oos.writeObject(obj);
int count;
byte[] buffer = new byte[8192];
while ((count = in.read(buffer)) > 0) {
out.write(buffer, 0, count);
}
服务器上:
ServerSocket sSocket = new ServerSocket(port);
Socket socket = sSocket.accept();
InputStream is = socket.getInputStream();
ObjectInputStream ois = new ObjectInputStream(is);
MyClass obj = (MyClass)ois.readObject();
byte arr[];
try {
while(arr = (byte[])ois.readObject()) {
//do something with arr
}
} catch(java.io.EOFException) {
// End of data
}
如果你需要在文件完成后发送更多的数据,你需要一种方法来计算文件包含的字节数。然后,您可以通过套接字预先将字节数发送到服务器。在服务器上,只读取文件的信息字节数,然后对要发送的其余数据执行相同操作。这种预先发送文件大小的策略是推荐的,主要用于进行任何数据传输。如果可以做到这一点,就不必依赖于捕获java.io.EOFException
来检测数据的末尾。