I am trying to receive a file that client sends using DataInputStream and write it into file.
(Client sends the file using DataInputStream write(byte[], len, off) method)
Here's how I am trying to do, but it does not receive full data.
InputStream in = s.getInputStream(); //s is Socket that is connected.
BufferedInputStream bis = new BufferedInputStream(in);
DataInputStream din = new DataInputStream(bis);
FileOutputStream fos = new FileOutputStream(directory+"/"+filename);
byte b = din.readByte();
while(b != -1){
fos.write(b);
b = din.readByte();
}
I know that the implementation above may not be elegant.
but I am really new to java so please forbear with me about bad style
(I really appreciate if you recommend better one if you know)
the result file is only 4KB whereas it should be 401KB
How should I fix this code so I can have my code working?
THank you very much.
you are reading a byte, and -1 (cast to a byte) is a valid byte value. you don't want to stop on -1, but should instead catch EOFException.
you test for -1 when using one of the standard InputStream.read() methods (which return int, not byte).
Related
This question already has answers here:
Java multiple file transfer over socket
(3 answers)
Closed 5 years ago.
I have the following code which transfers file via Sockets. How do I send the file name?
Socket socket = new Socket("localhost", port);//machine name, port number
File file = new File(fileName);
// Get the size of the file
long length = file.length();
if (length > Integer.MAX_VALUE)
{
System.out.println("File is too large.");
}
byte[] bytes = new byte[(int) length];
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream());
int count;
while ((count = bis.read(bytes)) > 0)
{
out.write(bytes, 0, count);
}
out.flush();
out.close();
fis.close();
bis.close();
socket.close();
You can invent your own protocol for your socket. If all you need is a filename and data, DataOutputStream.writeUTF is easiest:
BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream());
try (DataOutputStream d = new DataOutputStream(out)) {
d.writeUTF(fileName);
Files.copy(file.toPath(), d);
}
The peer must use the same protocol, of course:
BufferedInputStream in = new BufferedInputStream(socket.getInputStream());
try (DataInputStream d = new DataInputStream(in)) {
String fileName = d.readUTF();
Files.copy(d, Paths.get(fileName));
}
Use a character that can never be in a file name - such as a null (0x00, \0, whatever you want to call it). Then send a 64 bit integer that indicates how long, in bytes, the file is (make sure you don't run into buffer overflows, little endian/big endian issues, etc... just test all edge cases). Then send the file data. Then the ending socket will know which part is the file name, the file length and the file data, and will even be ready for the next file name if you want to send another.
(if file names can be arbitrary characters including control characters, ouch! Maybe send a 64 bit integer length of file name, the file name, a 64 bit integer length of file data, the file data, repeat ad infinitum?)
EDIT: To send a 64 bit integer over a socket, send its constituent bytes in a specific order, and make sure sender and receiver agree on the order. One example of how to do this is How to convert a Java Long to byte[] for Cassandra?
I tried to wrap a buffer which cause MalfuctionUTF and putting it on try-with resource closes the underlining socket stream and cause connection reset exceptionFollowing code worked for me
Client
DataOutputStream d = new DataOutputStream(out);
d.writeUTF(filename);
d.writeLong(length);
Server
DataInputStream d = new DataInputStream(in);
filename = d.readUTF();
fileLength = d.readLong();
I need help on my homework, any help will be much appreciated. I can send small files without a problem. But when i try to send let’s say a 1GB file byte array sends OutOfMemoryError so i need a better solution to send file from server to client. How can i improve this code and send big files, please help me.
Server Code:
FileInputStream fis = new FileInputStream(file);
byte[] fileByte = new byte[fis.available()]; //This causes the problem.
bytesRead = fis.read(fileByte);
oos = new ObjectOutputStream(sock.getOutputStream());
oos.writeObject(fileByte);
Client Code:
ois = new ObjectInputStream(sock.getInputStream());
byte[] file = (byte[]) ois.readObject();
fos = new FileOutputStream(file);
fos.write(file);
Don't read the whole file into memory, use a small buffer and write while you are reading the file:
BufferedOutputStream bos = new BufferedOutputStream(sock.getOutputStream())
File file = new File("asd");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
byte[] buffer = new byte[1024*1024*10];
int n = -1;
while((n = bis.read(buffer))!=-1) {
bos.write(buffer,0,n):
}
Use Buffered* to optimize the writing and reading from Streams
Just split the array to smaller chunks so that you don't need to allocate any big array.
For example you could split the array into 16Kb chunks, eg new byte[16384] and send them one by one. On the receiving side you would have to wait until a chunk can be fully read and then store them somewhere and start with next chunk.
But if you are not able to allocate a whole array of the size you need on server side you won't be able to store all the data that you are going to receive anyway.
You could also compress the data before sending it to save bandwidth (and time), take a look at ZipOutputStream and ZipInputStream.
Here's how I solved it:
Client Code:
bis=new BufferedInputStream(sock.getInputStream());
fos = new FileOutputStream(file);
int n;
byte[] buffer = new byte[8192];
while ((n = bis.read(buffer)) > 0){
fos.write(buffer, 0, n);}
Server Code:
bos= new BufferedOutputStream(sock.getOutputStream());
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
int n=-1;
byte[] buffer = new byte[8192];
while((n = bis.read(buffer))>-1)
bos.write(buffer,0,n);
Depending on whether or not you have to write the code yourself, there are existing libraries which solve this problem, e.g. rmiio. If you are not using RMI, just plain java serialization, you can use the DirectRemoteInputStream, which is kind of like a Serializable InputStream. (this library also has support for things like auto-magically compressing the data).
Actually, if you are only sending file data, you would be better off ditching the Object streams and use DataInput/DataOutput streams. first write an integer indicating the file length, then copy the bytes directly to the stream. on the receiving side, read the integer file length, then read exactly that many bytes.
when you copy the data between streams, use a small, fixed size byte[] to move chunks of data between the input and output streams in a loop. there are numerous examples of how to do this correctly available online (e.g. #ErikFWinter's answer).
I am trying to build a server program that receives file from client using DataInputStream and BufferedInputStream.
Here's my code and it falls into infinite loop, I think it's because of not using available() but I am not really sure.
DataInputStream din = new DataInputStream(new BufferedInputStream(s.getInputStream()));
//s is socket that connects fine
fos = new FileOutputStream(directory+"/"+filename);
byte b[] = new byte[512];
int readByte = din.read(b);
while(readByte != 1){
fos.write(b);
readByte = din.read(b);
//System.out.println("infinite loop...");
}
Can anyone tell me why it falls into infinite loop? if it is because of not using available
, would you please tell me how to use it? I actually googled, but I was confused with the usage. Thank you very much
I think you want to do while(readByte != -1). See the documentation (-1 means there is nothing more to read).
Response to Comment
This works for me:
FileInputStream in = new FileInputStream(new File("C:\\Users\\Rachel\\Desktop\\Test.txt"));
DataInputStream din = new DataInputStream(new BufferedInputStream(in));
FileOutputStream fos = new FileOutputStream("C:\\Users\\Rachel\\Desktop\\MyOtherFile.txt");
byte b[] = new byte[512];
while(din.read(b) != -1){
fos.write(b);
}
System.out.println("Got out");
As Rachel pointed out, the read method on DataInputStream returns the number of bytes successfully read in, or -1 if the end has been reached. The idiomatic way to loop until the end has been reached is while(readByte != -1) whereas you had 1 by mistake. If it is never the case that exactly 1 byte is read then this will be an infinite loop (readByte will never change from -1 once the end of the stream has been reached). If by chance there is an iteration where exactly 1 byte is read, this would have actually terminated early instead of going into an infinite loop.
Your question has already been answered but this code has another problem which is corrected below. The canonical stream copy loop looks like this:
while ((count = in.read(buffer)) > 0)
{
out.write(buffer, 0, count);
}
I would like to send image file from java server to android app using this code:
Server(Java):
File file = new File("./clique.jpg");
FileInputStream stream = new FileInputStream(file);
DataOutputStream writer = new DataOutputStream(socket.getOutputStream());
byte[] contextB = new byte[4096];
int n;
int i = 0;
while ( (n=stream.read(contextB))!=-1 ){
writer.write(contextB, 0, n);
writer.flush();
System.out.println(n);
i+=n;
}
writer.flush();
stream.close();
android app:
DataInputStream reader = new DataInputStream(socket.getInputStream());
byte[] buffer = new byte[4096];
ByteArrayOutputStream content = new ByteArrayOutputStream();
int n;
int i = 0;
reader = new DataInputStream(socket.getInputStream());
while ( (n=reader.read(buffer)) != null){
content.write(buffer, 0, n);
content.flush();
}
Utility.CreateImageFile(content.toByteArray());
What I noticed is that in the android app n which is the number of read bytes is not 4096 while I am sending from server byte blocks of 4096 size,also I can not get n=-1 which is the end of stream,it blocks until I close the app then I get n=-1.
Regarding the number of bytes you read at a time has nothing to do with the number of bytes you write -it very much depends on the network conditions and will be variable with every chunk (basically as many bytes managed to be transmitted in short period of time between your reads as many you will get in the read chunk.
Regarding the end of stream - in your server code you have forgotten to close the output stream (you only close the stream which is input stream - you should also close the writer which in turn will close the underlying output stream.
Two comments:
1) I would really recommend to use Buffered Readers/Writers wrapping the writes/readers - the code you will get will be nicer and you will not have to create/manage buffers yourself.
2) Use try {} finally and close your streams in finally clauses - this is the best practice that will make sure that you will close the streams and free resources even in case of problems while reading/writing.
You got a problem in your android code:
while ( (n=reader.read(buffer)) != null) {
n can not be null.
Use writer.close() instead of writer.flush() after your loop on the server.
I'm trying to familiarize myself with Java IO classes, so I wrote the code below:
public static void main(String[] args)throws IOException {
FileOutputStream fos = new FileOutputStream("fileIO.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
DataOutputStream dos = new DataOutputStream(bos);
//fos.write(9999);
//bos.write(9999);
dos.writeInt(9999);
dos.writeBytes("中文字(Chinese)\n");
dos.writeChars("中文字(Chinese)\n");
dos.flush();
FileInputStream fis = new FileInputStream("fileIO.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);
System.out.println(dis.readInt());
System.out.println(dis.readUTF());
}
Unfortunately, I get this:
9999
Exception in thread "main" java.io.EOFException
at java.io.DataInputStream.readFully(DataInputStream.java:180)
at java.io.DataInputStream.readUTF(DataInputStream.java:592)
at java.io.DataInputStream.readUTF(DataInputStream.java:547)
at IO.main(IO.java:34)
Could anyone point out why? Thanks.
Instead of
dos.writeBytes("中文字(Chinese)\n");
dos.writeChars("中文字(Chinese)\n");
you need
dos.writeUTF("中文字(Chinese)\n");
When you perform a readUTF, the first two byte are used for the length. This means if you have random bytes there (not from writeUTF) you will attempt to read very long string instead and as there is not enough data, you will get EOFException.
You can only use readUTF() to read items that were written with writeUTF().
This is true in general for readXXX() and writeXXX() for any XXX (unless you want to read the bytes of an int or some such and you know what you're doing).
I think this link will be helpful. The exception is thrown (from oracle docs) -
if this input stream reaches the end before reading all the bytes.
You haven't UTF char after Integer number in your file. When you're trying read UTF there is End of File so you have the exception.
Try in debug mode stop before you read and check manually to your file, what do you have then?