通过 java 套接字向 Android 设备发送文件时数据丢失
Data loss when sending file via java socket to an Android device
我在 java 中开发了一个基于套接字的聊天应用程序。我在 Android 上创建了两个客户端,桌面客户端和移动客户端,短信一切正常,现在我添加了文件发送功能,我在 Android 客户端上面临不确定的行为,有时它获取整个文件,有时它在读取文件时崩溃,或者我使用与桌面客户端完全相同的传输代码!
我的客户端应用程序以 json 格式向服务器发送命令,服务器以与结果相同的格式回复,在文件请求中,服务器回复包含 'files' 文件,因此客户端应输入 MFtp.ftpGetFile 方法,因为服务器在发送带有 file
as 字段的结果后自动进入 sendFile 方法
这是我的转账码!
MFtp.java
package com.molo.ftp;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class MFtp {
public static boolean ftpPut(File file,DataOutputStream out){
//File file=new File(fname);
if(!file.exists())
return false;
{
System.out.println("Begin sending file");
BufferedInputStream fin=null;
try{
fin= new BufferedInputStream(new FileInputStream(file));
String fname=file.getName();
long fileSize=file.length();
//send file name;
out.writeUTF(fname);
//send file size
out.writeLong(fileSize);
out.flush();//send completely those informations
int byteRead=0;
byte[] buffer=new byte[(int) Math.min(4096, fileSize)];
System.out.println("Buffer size: "+buffer.length);
while((byteRead=fin.read(buffer))>0){
out.write(buffer,0,byteRead);
out.flush();
System.out.println("BYTE READ AND WRITE TO SERVER :"+byteRead);
}
System.out.println("File totaly sent");
out.flush();
fin.close();
}catch(NumberFormatException e){
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
return true;
}
public static boolean ftpPut(String fname,DataOutputStream out){
File file=new File(fname);
if(!file.exists())
return false;
{
System.out.println("Begin sending file");
BufferedInputStream fin=null;
try{
fin= new BufferedInputStream(new FileInputStream(file));
long fileSize=file.length();
fname=file.getName();
//send file name;
out.writeUTF(fname);
//send file size
out.writeLong(fileSize);
out.flush();//send completely those informations
int byteRead=0;
byte[] buffer=new byte[(int) Math.min(4096, fileSize)];
System.out.println("Buffer size: "+buffer.length);
while((byteRead=fin.read(buffer))>0){
out.write(buffer,0,byteRead);
out.flush();
System.out.println("BYTE READ AND WRITE TO SERVER :"+byteRead);
}
System.out.println("File totaly sent");
out.flush();
fin.close();
}catch(NumberFormatException e){
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
return true;
}
public static File ftpGetFile(DataInputStream din,String dir){
//read file size from the client
try{
//read file name
String fname=din.readUTF();
//read filename
long fileSize=din.readLong();
File outPut=new File(dir+"/"+fname);
BufferedOutputStream fout=null;
fout= new BufferedOutputStream(new FileOutputStream(outPut));
long byteRestants=fileSize;
byte[] buffer=new byte[(int) Math.min(4096, fileSize)];
System.out.println("Start receiving file: "+fname+" / "+fileSize);
int byteToRead=0;
while(byteRestants>0){
byteToRead=din.read(buffer, 0,(int)Math.min(buffer.length, byteRestants));
byteRestants-=byteToRead;
fout.write(buffer,0,byteToRead);
System.out.println("Byte restant: "+byteRestants);
}
fout.close();
return outPut;
}catch(NumberFormatException e){
return null;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
}
这是我的客户端读取线程代码,我在其中调用 MFtp getFile 方法:
private class ReadThread extends Thread{
@Override
public void run() {
if(reader==null){
try {
System.out.println("Openning");
reader=new DataInputStream(new BufferedInputStream(socket.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
while(go){
try {
String line= readLine(reader);
if(line==null)
break;
System.out.println("Client.ReadThread:line "+line);
JSONObject result= new JSONObject(line);
if(!result.has("hash"))
continue;
Response r=new Response(result.getLong("hash"),result.getInt("status"), result.get("data"));
//System.out.println("Result: "+result.toString());
MediaFile[] medias=null;
if(result.has("files")){
JSONArray list=result.getJSONArray("files");
System.out.println("Client.ReadThread:line "+"Has Media :"+list.length());
// List<MediaFile> files= new ArrayList<>();
medias=new MediaFile[list.length()];
for(int i =0;i<list.length();i++){
JSONObject obj=list.getJSONObject(i);
MediaFile m=new MediaFile();
m.name=obj.getString("name");
m.size=obj.getLong("size");
m.type=obj.getString("type");
Log.e("Client.medias.receiving","m.size: "+m.size+" m.name "+m.name+" m.type : "+m.type);
m.file= MFtp.ftpGetFile(reader, MainActivity.TEMP_DIR);
if(m.file!=null){
m.absolutPath=m.file.getAbsolutePath();
Log.e("Client.received: ",m.absolutPath);
}else{
Log.e("Client.received: ","Failed to save file");
System.out.println("Client.received: "+"Failed to save file");
}
}
}
r.medias=medias;
if(queryManager!=null && r.hash>0){
queryManager.onResult(r);
}
else if(listener!=null && r.hash<=0)
listener.onReceiveNewMessage(r);
}catch(SocketException e){
if(listener!=null)
listener.onSocketExceptionWhenRead(socket);
}
catch (IOException e) {
if(listener!=null)
listener.onIOExceptionWhenRead(socket);
} catch (JSONException e) {
e.printStackTrace();
}
try {
Thread.sleep(40);
} catch (InterruptedException e) {
// TODO: handle exception
//readThread.interrupt();
break;
}
}
}
}
reader线程在socket上列出,当它从socket接收到一条新消息时,它以json格式解析它,然后创建一个新的Response对象,但是如果响应包含文件字段,这意味着服务器将在代码
之后发送一个文件
...............
else if(command.matches("getUserProfil")){
if(request.has("user")){
String userLog=request.get("user").asText();
ObjectNode n=mapper.createObjectNode();
Membre m=memberManager.getOne(userLog);
MediaFile f=new MediaFile();
f.file=new File(m.getProfil());
f.name=f.file.getName();
f.size=f.file.length();
f.type="image/png";
n.put("cache", true);
System.out.println("SIZE: "+f.size+" ;; "+f.size);
println(mOut, createResponse(comId, MyStandarMessageType.OK, n,f));//will send a json string with "files" as a field
mOut.flush();
sendFile(f);
}
}
...........the sendFile method.........
private void sendFile(MediaFile... files) throws IOException {
if(files!=null && files.length >0){
System.out.println("SendFile");
for(MediaFile f:files){
System.out.println("Start sending file");
MFtp.ftpPut(f.file,mOut);
}
}
}
在桌面上没有问题,但是在Android客户端上,有时可以成功接收文件,但更多时候没有!
最不合逻辑的是,在客户端读取线程中,进入 ftpGetFile 方法后,调用行 String line=readLine(reader) (在 readThread 中)而不是 String fname=din.readUTF() 在ftpGetFile 方法
这是一种情况下的日志猫,但有时文件接收完整
I/System.out: Client.ReadThread:line {"hash":2,"status":200,"data":{"cache":true},"files":[{"type":"image/png","size":1875,"name":"prof_molo_1492209637904.png"}]}
I/System.out: Client.ReadThread:line Has Media :1
I/System.out: Client.ReadThread:line prof_molo_1492209637904.png
I/System.out: fGetFile
I/System.out: MFt.ftpGetFile name :
I/System.out: MFt.ftpGetFile size: 122915152
W/System.err: java.io.FileNotFoundException: /storage/sdcard0/molochat/temp: open failed: EISDIR (Is a directory)
W/System.err: org.json.JSONException: Value prof_molo_1492209637904.png of type java.lang.String cannot be converted to JSONObject
W/System.err: at libcore.io.IoBridge.open(IoBridge.java:416)
W/System.err: at java.io.FileOutputStream.<init>(FileOutputStream.java:88)
W/System.err: at java.io.FileOutputStream.<init>(FileOutputStream.java:73)
W/System.err: at com.molo.app.chat.ftp.MFtp.ftpGetFile(MFtp.java:99)
W/System.err: at com.molo.app.chat.net.Client$ReadThread.run(Client.java:228)
W/System.err: Caused by: libcore.io.ErrnoException: open failed: EISDIR (Is a directory)
W/System.err: at org.json.JSON.typeMismatch(JSON.java:111)
W/System.err: at org.json.JSONObject.<init>(JSONObject.java:158)
W/System.err: at org.json.JSONObject.<init>(JSONObject.java:171)
W/System.err: at com.molo.app.chat.net.Client$ReadThread.run(Client.java:209)
W/System.err: at libcore.io.Posix.open(Native Method)
W/System.err: at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
W/System.err: at libcore.io.IoBridge.open(IoBridge.java:400)
W/System.err: ... 4 more
E/Client.received:: Failed to save file
I/System.out: Client.received: Failed to save file
:'(
请帮忙!
这是我的错误,当应用程序进入暂停状态时我没有正确关闭线程,因此在恢复时创建了一个新的读取线程,因此数据没有按预期处理!
我在 java 中开发了一个基于套接字的聊天应用程序。我在 Android 上创建了两个客户端,桌面客户端和移动客户端,短信一切正常,现在我添加了文件发送功能,我在 Android 客户端上面临不确定的行为,有时它获取整个文件,有时它在读取文件时崩溃,或者我使用与桌面客户端完全相同的传输代码!
我的客户端应用程序以 json 格式向服务器发送命令,服务器以与结果相同的格式回复,在文件请求中,服务器回复包含 'files' 文件,因此客户端应输入 MFtp.ftpGetFile 方法,因为服务器在发送带有 file
as 字段的结果后自动进入 sendFile 方法
这是我的转账码!
MFtp.java
package com.molo.ftp;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class MFtp {
public static boolean ftpPut(File file,DataOutputStream out){
//File file=new File(fname);
if(!file.exists())
return false;
{
System.out.println("Begin sending file");
BufferedInputStream fin=null;
try{
fin= new BufferedInputStream(new FileInputStream(file));
String fname=file.getName();
long fileSize=file.length();
//send file name;
out.writeUTF(fname);
//send file size
out.writeLong(fileSize);
out.flush();//send completely those informations
int byteRead=0;
byte[] buffer=new byte[(int) Math.min(4096, fileSize)];
System.out.println("Buffer size: "+buffer.length);
while((byteRead=fin.read(buffer))>0){
out.write(buffer,0,byteRead);
out.flush();
System.out.println("BYTE READ AND WRITE TO SERVER :"+byteRead);
}
System.out.println("File totaly sent");
out.flush();
fin.close();
}catch(NumberFormatException e){
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
return true;
}
public static boolean ftpPut(String fname,DataOutputStream out){
File file=new File(fname);
if(!file.exists())
return false;
{
System.out.println("Begin sending file");
BufferedInputStream fin=null;
try{
fin= new BufferedInputStream(new FileInputStream(file));
long fileSize=file.length();
fname=file.getName();
//send file name;
out.writeUTF(fname);
//send file size
out.writeLong(fileSize);
out.flush();//send completely those informations
int byteRead=0;
byte[] buffer=new byte[(int) Math.min(4096, fileSize)];
System.out.println("Buffer size: "+buffer.length);
while((byteRead=fin.read(buffer))>0){
out.write(buffer,0,byteRead);
out.flush();
System.out.println("BYTE READ AND WRITE TO SERVER :"+byteRead);
}
System.out.println("File totaly sent");
out.flush();
fin.close();
}catch(NumberFormatException e){
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
return true;
}
public static File ftpGetFile(DataInputStream din,String dir){
//read file size from the client
try{
//read file name
String fname=din.readUTF();
//read filename
long fileSize=din.readLong();
File outPut=new File(dir+"/"+fname);
BufferedOutputStream fout=null;
fout= new BufferedOutputStream(new FileOutputStream(outPut));
long byteRestants=fileSize;
byte[] buffer=new byte[(int) Math.min(4096, fileSize)];
System.out.println("Start receiving file: "+fname+" / "+fileSize);
int byteToRead=0;
while(byteRestants>0){
byteToRead=din.read(buffer, 0,(int)Math.min(buffer.length, byteRestants));
byteRestants-=byteToRead;
fout.write(buffer,0,byteToRead);
System.out.println("Byte restant: "+byteRestants);
}
fout.close();
return outPut;
}catch(NumberFormatException e){
return null;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
}
这是我的客户端读取线程代码,我在其中调用 MFtp getFile 方法:
private class ReadThread extends Thread{
@Override
public void run() {
if(reader==null){
try {
System.out.println("Openning");
reader=new DataInputStream(new BufferedInputStream(socket.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
while(go){
try {
String line= readLine(reader);
if(line==null)
break;
System.out.println("Client.ReadThread:line "+line);
JSONObject result= new JSONObject(line);
if(!result.has("hash"))
continue;
Response r=new Response(result.getLong("hash"),result.getInt("status"), result.get("data"));
//System.out.println("Result: "+result.toString());
MediaFile[] medias=null;
if(result.has("files")){
JSONArray list=result.getJSONArray("files");
System.out.println("Client.ReadThread:line "+"Has Media :"+list.length());
// List<MediaFile> files= new ArrayList<>();
medias=new MediaFile[list.length()];
for(int i =0;i<list.length();i++){
JSONObject obj=list.getJSONObject(i);
MediaFile m=new MediaFile();
m.name=obj.getString("name");
m.size=obj.getLong("size");
m.type=obj.getString("type");
Log.e("Client.medias.receiving","m.size: "+m.size+" m.name "+m.name+" m.type : "+m.type);
m.file= MFtp.ftpGetFile(reader, MainActivity.TEMP_DIR);
if(m.file!=null){
m.absolutPath=m.file.getAbsolutePath();
Log.e("Client.received: ",m.absolutPath);
}else{
Log.e("Client.received: ","Failed to save file");
System.out.println("Client.received: "+"Failed to save file");
}
}
}
r.medias=medias;
if(queryManager!=null && r.hash>0){
queryManager.onResult(r);
}
else if(listener!=null && r.hash<=0)
listener.onReceiveNewMessage(r);
}catch(SocketException e){
if(listener!=null)
listener.onSocketExceptionWhenRead(socket);
}
catch (IOException e) {
if(listener!=null)
listener.onIOExceptionWhenRead(socket);
} catch (JSONException e) {
e.printStackTrace();
}
try {
Thread.sleep(40);
} catch (InterruptedException e) {
// TODO: handle exception
//readThread.interrupt();
break;
}
}
}
}
reader线程在socket上列出,当它从socket接收到一条新消息时,它以json格式解析它,然后创建一个新的Response对象,但是如果响应包含文件字段,这意味着服务器将在代码
之后发送一个文件...............
else if(command.matches("getUserProfil")){
if(request.has("user")){
String userLog=request.get("user").asText();
ObjectNode n=mapper.createObjectNode();
Membre m=memberManager.getOne(userLog);
MediaFile f=new MediaFile();
f.file=new File(m.getProfil());
f.name=f.file.getName();
f.size=f.file.length();
f.type="image/png";
n.put("cache", true);
System.out.println("SIZE: "+f.size+" ;; "+f.size);
println(mOut, createResponse(comId, MyStandarMessageType.OK, n,f));//will send a json string with "files" as a field
mOut.flush();
sendFile(f);
}
}
...........the sendFile method.........
private void sendFile(MediaFile... files) throws IOException {
if(files!=null && files.length >0){
System.out.println("SendFile");
for(MediaFile f:files){
System.out.println("Start sending file");
MFtp.ftpPut(f.file,mOut);
}
}
}
在桌面上没有问题,但是在Android客户端上,有时可以成功接收文件,但更多时候没有! 最不合逻辑的是,在客户端读取线程中,进入 ftpGetFile 方法后,调用行 String line=readLine(reader) (在 readThread 中)而不是 String fname=din.readUTF() 在ftpGetFile 方法
这是一种情况下的日志猫,但有时文件接收完整
I/System.out: Client.ReadThread:line {"hash":2,"status":200,"data":{"cache":true},"files":[{"type":"image/png","size":1875,"name":"prof_molo_1492209637904.png"}]}
I/System.out: Client.ReadThread:line Has Media :1
I/System.out: Client.ReadThread:line prof_molo_1492209637904.png
I/System.out: fGetFile
I/System.out: MFt.ftpGetFile name :
I/System.out: MFt.ftpGetFile size: 122915152
W/System.err: java.io.FileNotFoundException: /storage/sdcard0/molochat/temp: open failed: EISDIR (Is a directory)
W/System.err: org.json.JSONException: Value prof_molo_1492209637904.png of type java.lang.String cannot be converted to JSONObject
W/System.err: at libcore.io.IoBridge.open(IoBridge.java:416)
W/System.err: at java.io.FileOutputStream.<init>(FileOutputStream.java:88)
W/System.err: at java.io.FileOutputStream.<init>(FileOutputStream.java:73)
W/System.err: at com.molo.app.chat.ftp.MFtp.ftpGetFile(MFtp.java:99)
W/System.err: at com.molo.app.chat.net.Client$ReadThread.run(Client.java:228)
W/System.err: Caused by: libcore.io.ErrnoException: open failed: EISDIR (Is a directory)
W/System.err: at org.json.JSON.typeMismatch(JSON.java:111)
W/System.err: at org.json.JSONObject.<init>(JSONObject.java:158)
W/System.err: at org.json.JSONObject.<init>(JSONObject.java:171)
W/System.err: at com.molo.app.chat.net.Client$ReadThread.run(Client.java:209)
W/System.err: at libcore.io.Posix.open(Native Method)
W/System.err: at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
W/System.err: at libcore.io.IoBridge.open(IoBridge.java:400)
W/System.err: ... 4 more
E/Client.received:: Failed to save file
I/System.out: Client.received: Failed to save file
:'(
请帮忙!
这是我的错误,当应用程序进入暂停状态时我没有正确关闭线程,因此在恢复时创建了一个新的读取线程,因此数据没有按预期处理!