在原有基础上加入文件的传输功能。
从客户端传送到服务器后,服务器接受文件,再转发给其他客户端,客户端接受后服务器删除文件。
涉及文件的读取与关闭。
1.原来传输的改进
1.1服务器需要区分传输的是文件还是信息。
在传输之前先传输一个0/1让其判断是否为文件,其中0表示文件。
2.客服端clint:
在里面添加了一个传输文件的方法outputFIle();
File类只存储文件的相关信息,如文件路径等等。因此文件的传输需要将文件的内容发送过去。
使用fileinputstream来读取文件的信息,DataOutputStream其有一些便捷的方式将数据转化为字节流,用他来传输文件内容。
现将文件的名字,以及群聊或者私聊及其对象传送过去,接着写入文件。定义一个1024大小的字节数组。使用read方法进行读取,此方法返回读取的长度。当读完文件后返回-1。读取1024字节大小后使用dos.write()写入。最后需要关闭文件的读取流,释放资源。
public void outputFile(File file,int index){
try {
//让其判断是文件还是信息
os.write(0);//表示文件
fis = new FileInputStream(file);
dos = new DataOutputStream(os);
//文件名,大小等属性
dos.writeUTF(file.getName());
dos.flush();//确保数据立即发送
//发送群聊或者私聊
String s;
if (IMG[1] == 0) {
s = IMG[1]+"";
} else {
s = PORT.get(index);
}
dos.writeUTF(s);
dos.flush();
dos.writeLong(file.length());
dos.flush();
//传输文件
System.out.println("传输文件");
byte[] bytes = new byte[1024];
int length ;
//读取文件file,并传输
while((length = fis.read(bytes,0, bytes.length)) != -1){//每次读取1024个字节
dos.write(bytes,0,length);
dos.flush();
}
//释放文件读取流
fis.close();
System.out.println("发送成功");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
2.2按钮监听
在按钮中增添了发送文件的按钮。
并创建一个文件选择器的对象,选择文件后记录在file中,并调用上诉发送文件的方法。
else if (ae.equals("发送文件")) {
System.out.println("发送文件");
JFileChooser jfc = new JFileChooser();
int result = jfc.showOpenDialog(null);
if (result == JFileChooser.APPROVE_OPTION) {
File file = jfc.getSelectedFile();
//发送给服务器
int index = groupOrPrivate();
clint.outputFile(file, index);
}
}
2.3服务端的接受线程:
与发送类似先判断是消息还是文件,调用对应方法。
@Override
public void run() {
while (true) {
try {
int type = is.read();
if (type == 1) {//消息
getMsg();
} else if (type == 0) {//文件
saveFile();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
存储文件方法saveFile();
与写入类似,只不过使用datainputstream进行读取,fileoutputstream进行写入。
接受到文件名后存入fileName中 与大小后 创建相应文件名对象。同读取文件方法类似的写入方法。不过需要判断已经接受字节的大小,来停止文件的接受。因为接受文件的传输没有读取返回的-1。也可以使用接受到特定的字节时停止。最后关闭文件输出流。
public void saveFile() {
try {
System.out.println("接受文件");
dis = new DataInputStream(is);
String fileName = dis.readUTF();
long fileLength = dis.readLong();
File file = new File("C:\\Users\\15697\\IdeaProjects\\Pro24\\src\\ChatV3\\Client\\GetFile" + File.separatorChar + fileName);
fos = new FileOutputStream(file);
System.out.println("检测到文件名为:" + fileName + "开始接受文件");
byte[] bytes = new byte[1024];
int length;
long readLength = 0;
while ((length = dis.read(bytes, 0, bytes.length)) != -1) {
fos.write(bytes, 0, length);
fos.flush();
readLength +=length;
if(readLength >= fileLength){
break;
}
}
//关闭文件输出流
fos.close();
System.out.println("文件接受完毕");
sendMsg("-----接受到文件:"+fileName+"请查看------");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
服务端:
发送给客户端数据同样再加上消息还是文件的判定。
同时接受信息与客户端接受信息一致,需要接受文件。接受后还需要发送给指定用户。
@Override
public void run() {
while (true) {
getIMG();
}
}
//接受消息
public void getIMG() {
try {
int type = is.read();//判断文件 还是信息
if (type == 1) {
System.out.println("接受消息");
getMessage();
} else if (type == 0) {
System.out.println("为文件");
//接受文件
getFile();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
getFile();先进行文件的获取,接着判断接受的对象名s为0则是群聊,其他的则是其他客户端的端口号。为群聊则调用
groupOutputFile(file);
私聊则调用先确定私聊的对象接着调用
outputFile(file);
即可。
public void getFile() {
try {
dis = new DataInputStream(is);
String fileName = dis.readUTF();
String s = dis.readUTF();//私发用户
System.out.println("对象:" + s);
long fileLength = dis.readLong();
File file = new File("C:\\Users\\15697\\IdeaProjects\\Pro24\\src\\ChatV3\\Server\\temFile" + File.separatorChar + fileName);
fos = new FileOutputStream(file);
System.out.println("检测到文件名为:" + fileName + "开始接受文件");
byte[] bytes = new byte[1024];
int length;
long readLength = 0;
while ((length = dis.read(bytes, 0, bytes.length)) != -1) {//客户端读取-1时以及停止所以未发送过来,因此会陷入while中等待读取,需要我们加入一个-1
fos.write(bytes, 0, length);
fos.flush();
readLength += length;
if(readLength >= fileLength){
break;
}
}
System.out.println("文件接受完毕");
//接受发送对象
//dis.close();
if (s.equals("0")) {//群聊
System.out.println("群发文件");
//文件群聊发送
groupOutputFile(file);
} else {
//私发
int size = sockets.size();
for (int i = 0; i < size; i++) {
System.out.println("查找私发文件对象");
Socket temSocket = sockets.poll();
if (s.equals(String.valueOf(temSocket.getPort()))) {//给私聊对象发送
//发送信号给客户端是否接受文件
os = temSocket.getOutputStream();
outputFile(file);
//删除文件
deleteFile(file);
}
sockets.offer(temSocket);
}
//发送给消息对象
os = socket.getOutputStream();
output("文件发送成功");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public synchronized void groupOutputFile(File file) {
try {
int size = sockets.size();
for (int i = 0; i < size; i++) {
System.out.println("多少个用户端:" + sockets.size());
Socket temSocket = sockets.poll();
os = temSocket.getOutputStream();
outputFile(file);
sockets.offer(temSocket);
}
//删除文件
deleteFile(file);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void outputFile(File file) {
try {
//让其判断是文件还是信息
os.write(0);//表示文件
fis = new FileInputStream(file);
dos = new DataOutputStream(os);
//文件名,大小等属性
dos.writeUTF(file.getName());
dos.flush();//确保数据立即发送
dos.writeLong(file.length());
dos.flush();
//传输文件
System.out.println("传输文件");
byte[] bytes = new byte[1024];
int length;
//读取文件file,并传输
while ((length = fis.read(bytes, 0, bytes.length)) != -1) {//每次读取1024个字节
dos.write(bytes, 0, length);
dos.flush();
}
System.out.println("发送成功");
fis.close();
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void deleteFile(File file){
//删除文件
if(file.exists()){
boolean isdelete = file.delete();
if(isdelete){
System.out.println("文件已接受,删除文件成功");
}else {
System.out.println("文件正在被使用,删除失败");
}
}
}
即可完成文件的传输。
注:文件的的写入与读取后将相应流关闭。关闭后服务器才能删除缓存文件。