通过socket java发送大文件

时间:2014-09-07 11:13:58

标签: java sockets stream filestream

我正在尝试通过socket传输更大的文件。我将以块的形式传输文件。如代码中所示。link

int count;
byte[] buffer = new byte[8192];
while ((count = in.read(buffer)) > 0)
{
  out.write(buffer, 0, count);
}

在发送此文件之前,我想发送一个包含文件详细信息的对象。我应该使用哪个流发送Object + File。

我是溪流的新手,我可以获得任何示例代码。

我可以先发送对象的字节长度来读取对象,保存并发送文件数据。是否可以使用任何示例代码?

由于 421

3 个答案:

答案 0 :(得分:0)

我不确定使用Java的序列化机制是否是进行简单文件传输的最佳方式。正如您的问题所示,您尝试避免随时将整个文件保留在内存中。这可以通过使用代理模式的对象来完成,但如果您只想传输文件,这可能不是最直接的解决方案。 (此外,它还可以有效地将您的对等体绑定在Java中实现。)

相反,为什么不看看一个非常成功的协议,它完全符合你的需要:HTTP。

Content-Type: application/octet-stream
Content-Length: 542183

542183 bytes of data follow...

为元数据头编写解析器应该不会太难。

答案 1 :(得分:0)

您需要确保写/读的顺序。如果客户端上为write an object -> write raw bytes,则服务器上为read an object -> read raw bytes。阅读时,ObjectInputStream应该能够找到序列化对象数据的边界。

如果你想长时间保持套接字连接并多次使用它的流,那么将套接字Output/InputStream包裹在ObjectOutput/InputStream中并不是一个好主意IMO。关闭对象流时,它也会关闭基础流。

因此,您可能希望首先编写序列化对象数据的长度(文件长度包含在对象中,因此您不需要明确地写入它),例如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;
    }
}

我没有在我的答案中测试示例代码..只是为了表明这个想法。

答案 2 :(得分:-1)

类MyClass应该实现Serializable接口。然后,可以使用writeObject和readObject方法将此类的对象写入ObjectOutputStream并从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来检测数据结尾。