Wrong file transfer over java Socket
this afternoon I wrote this class whose aim is give a easy way to exchange send a file over TCP Socket.
The problem it that, despite the final file size is correct, the content in wrong: precisely the destination file is made of various copies of the first buffer sent over Socket. My class is simple: it calculates Q and R based on buffer size and sends this number together original filename to the client. I used a byte array to send data over Socket.package it.s4sytems.java;
import java.io.*;
import java.net.*;
public class FileOverObjectStream
{
private File file;
private int bufferSize = 4*1024*1024; //4MB default, comunque è stabilito dal sender
private static class Info implements Serializable
{
public String fileName;
public long q;
public int r;
public int bufferSize;
}
public FileOverObjectStream(File file)
{
this.file = file;
}
public FileOverObjectStream(File file, int bufferSize)
{
this(file);
this.bufferSize = bufferSize;
}
public void sendFile(Socket socket) throws IOException
{
socket.getInputStream();
sendFile( socket.getOutputStream() );
}
public void sendFile(OutputStream outStream)throws IOException
{
开发者_运维知识库 sendFile( new ObjectOutputStream(outStream) );
}
public void sendFile(ObjectOutputStream objOutStream) throws IOException
{
BufferedInputStream in = new BufferedInputStream( new FileInputStream(file) );
byte[] buffer = new byte[bufferSize];
Info info = new Info();
info.fileName = file.getName();
info.bufferSize = bufferSize;
info.q = file.length() / bufferSize;
info.r = (int) file.length() % bufferSize;
objOutStream.writeObject(info);
for(long i=0; i<info.q; i++)
{
in.read(buffer);
objOutStream.writeObject(buffer);
objOutStream.flush();
}
in.read( buffer = new byte[info.r]);
objOutStream.writeObject(buffer);
objOutStream.flush();
in.close();
}
public String receiveFile(Socket socket) throws IOException, ClassNotFoundException
{
socket.getOutputStream();
return receiveFile( socket.getInputStream() );
}
public String receiveFile(InputStream inStream) throws IOException, ClassNotFoundException
{
return receiveFile( new ObjectInputStream(inStream) );
}
public String receiveFile(ObjectInputStream objInStream) throws IOException, ClassNotFoundException
{
BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream(file) );
Info info = (Info) objInStream.readObject();
for(long i=0; i<info.q+1; i++)
{
byte[] buffer = (byte[]) objInStream.readObject();
out.write( buffer );
}
out.close();
return info.fileName;
}
}
I created two classes to make some try...
import it.s4sytems.java.*;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server
{
public static void main(String arg[]) throws IOException
{
ServerSocket ss = new ServerSocket(18000);
while(true)
{
Socket s = ss.accept();
File file = new File("G:\\HCHCK_72_5.38.part04.rar");
FileOverObjectStream sender = new FileOverObjectStream(file);
sender.sendFile(s);
s.close();
}
}
}
and client...
import it.s4sytems.java.*;
import java.io.*;
import java.net.*;
public class Client
{
public static void main(String arg[]) throws IOException, ClassNotFoundException
{
Socket s = new Socket("localhost", 18000);
String matricola = "616002424";
File directory = new File(System.getProperty("user.dir") + "\\" + matricola);
directory.mkdir();
File file = File.createTempFile("7897_", null, directory);
String originalName = new FileOverObjectStream(file).receiveFile(s);
System.out.println(originalName);
s.close();
File file2 = new File(directory, originalName);
System.out.println( file.renameTo( file2 ) );
System.out.println( file.getAbsoluteFile());
System.out.println( file2.getAbsoluteFile());
}
}
Probably it's a stupid thing, but I can't see it, so I need your help, please.
Thank you
I don't think ObjectOutputStream
is suitable in your use case. Unless I missed something. In general, try to use some good library for IO such as Apache Commons IO. It has methods that would always do the right thing. Look at IOUtils for example.
Some errors to highlight (they would not happen with good library)
in.read(buffer)
is not guaranteed to read exact number of bytes. You must check its result and only write correct number.- You write buffer object to ObjectOutputStream with
writeObject
. That writes serialized byte buffer not raw sequence of bytes.
Your ObjectInput/OutputStream code is flawed in all the ways Alex noted. I wouldn't use it at all, I would just use raw I/O. The canonical way to copy a stream in Java is as follows:
int count;
byte[] buffer = new byte[8192]; // or more, but megabytes is pointless as the network will packetize anyway
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}
Use that same code when both sending and receiving the file. If you want to send > 1 file per connection, you need to prefix all that by sending the file name and length, which you can do with DataOutputStream.writeUTF()/writeLong()
, and DataInputStream.readUTF()/readLong()
at the receiver, and modify the loop control to read exactly that many bytes:
long remaining = size; // the file size read from the network
while ((count = in.read(buffer, 0, remaining > buffer.length ? buffer.length : (int)remaining)) > 0)
{
out.write(buffer, 0, count);
remaining -= count;
}
精彩评论