Java-模拟客户端从服务器下载指定文件名的文件(有个挺坑的点!!)
需求
/*
1.编写服务器端和客户端;
2.客户端输入音乐文件名称,返回对应的音乐文件,若服务器不存在该音乐文件,则返回默认的音乐文件;
3.客户端收到文件后,保存到本地./hsp_ch21/Practices/music/目录下;
*/
服务器端实现
/**
* @author: sea-365
* @date: 2023/5/2 16:08
*/
public class download_server {
public static void main(String[] args) throws IOException {
/*
1.编写服务器端和客户端;
2.客户端输入音乐文件名称,返回对应的音乐文件,若服务器不存在该音乐文件,则返回默认的音乐文件;
3.客户端收到文件后,保存到本地./hsp_ch21/Practices/music/目录下;
*/
//监听9999号端口
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("连接服务器...");
Socket socket = serverSocket.accept();
System.out.println("连接成功!");
//接收客户端的消息,根据该消息返回相应的音乐文件
String server_fileDirectoryPath = "./hsp_ch21/testfile/music";
File filedirectory = new File(server_fileDirectoryPath);
if(!filedirectory.exists()){
filedirectory.mkdirs();
}
//获取服务器的所有文件的名称(不包含父目录路径 和 文件后缀名)
String[] server_fileList = filedirectory.list();
for (String s : server_fileList) {
System.out.println(s);
}
String request_fileName = "";
BufferedInputStream bufferedInputStream = new BufferedInputStream(socket.getInputStream());
int readLen = 0;
byte[] buf = new byte[1024];
String data = "";
while((readLen = bufferedInputStream.read(buf)) != -1){
data = new String(buf, 0, readLen);
request_fileName += data;
}
socket.shutdownInput();
//查找该文件名称,若找到则发送该文件给客户端,否则默认发送第一个文件
int flag = 0;
for (String s : server_fileList) {
if(request_fileName.equals(s)){
flag = 1;
break;
}
}
if(flag == 0)
request_fileName = server_fileList[0];
System.out.println("【" + request_fileName + "】");
BufferedInputStream file_bis = new BufferedInputStream(new FileInputStream(filedirectory + "/" + request_fileName));
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
//先发送当前文件名称+空格,空格是为了客户端用于区分文件名称和文件数据的
bufferedOutputStream.write((request_fileName + " ").getBytes());
int len_file = 0;
byte[] buf_file = new byte[1024];
while((len_file = file_bis.read(buf_file)) != -1){
bufferedOutputStream.write(buf_file, 0, len_file);
}
//带缓冲区,一定记得手动刷新一下!!!
bufferedOutputStream.flush();
socket.shutdownOutput();
file_bis.close();
socket.close();
serverSocket.close();
}
}
客户端实现
/**
* @author: sea-365
* @date: 2023/5/2 16:08
*/
public class download_client {
public static void main(String[] args) throws IOException {
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
//发送文件名,请求相应文件
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
bufferedOutputStream.write("十年-陈奕迅.flac".getBytes());
//带缓冲区,一定记得手动刷新一下!!!
bufferedOutputStream.flush();
socket.shutdownOutput();
//接收文件
String fileDirectoryPath = "./hsp_ch21/Practices/music";
File fileDirectory = new File(fileDirectoryPath);
if(!fileDirectory.exists()){
fileDirectory.mkdirs();
}
BufferedOutputStream file_bos = null;
BufferedInputStream bufferedInputStream = new BufferedInputStream(socket.getInputStream());
int readLen = 0;
byte[] buf = new byte[1024];
boolean isFirst = true;
String fileName;
while((readLen = bufferedInputStream.read(buf)) != -1){
if(isFirst){//第一次读取数据,包含了文件名称和部分文件数据,需要处理
//提取文件名
String s = new String(buf, 0, readLen);
int spaceIndex = s.indexOf(" ");
fileName = s.substring(0, spaceIndex);
System.out.println("收到文件:" + fileName);
isFirst = false;
file_bos = new BufferedOutputStream(new FileOutputStream(fileDirectory + "/" + fileName));
//将剩余部分写入文件===此处巨坑!!!
//千万不能直接将上述byte数组转换来字符串进行拆分,因为会涉及字符编码问题。
// 《《应该直接将原来的byte数组进行拆分!!!!》》
file_bos.write(buf,spaceIndex + 1, readLen-spaceIndex -1);
}
else
file_bos.write(buf, 0, readLen);
}
socket.shutdownInput();
file_bos.close();
socket.close();
}
}
坑!!
-
坑在客户端第一次接收服务器端返回的数据时:
- 由于第一次返回的数据中,包含了文件名称和部分文件数据,需要进行处理;
- 分别处理文件名和部分文件数据;
- 提取文件名:通过将第一次读取到的1024个字节的字节数组转为字符串,然后通过第一个空格拆出文件名即可(服务器端设定的规则);
- 提取剩余部分的文件数据,并写入文件:
- 千万不能直接将上述byte数组转换来字符串进行拆分,因为会涉及字符编码问题。
- 《应该直接将原来的byte数组进行拆分!!!!》
-
记录一下坑,因为这个坑,同学喊我打球都没有去!!