前言
- 基于Socket的TCP协议简单实现客户端和服务器之间的文件传输,实现上传,下载文件。
效果:
- 客户端:
- 服务端:
思路:
客户端:
- 打开客户端后,发送读取服务器文件列表消息给服务器,然后服务器把文件列表返回返回,然后客户端再在界面上显示即可。之后客户端可进行下载,上传文件,刷新文件列表操作。所以客户端这里有三种类型消息,为了能方便服务器识别客户端消息的类型然后进行相应处理,简单规范化双方通信协议。如下:然后要进行相应操作发送相应消息给服务器即可。
通信格式:
1) 下载文件消息格式:@action=Download["fileName":"fileSize":"result"]
参数说明:
fileName 要下载的文件的名字
fileSize 要下载的文件的大小
result 下载许可
2) 上传文件消息格式: @action=Upload["fileName":"fileSize":result]
参数说明:
fileName 要上传的文件的名字
* fileSize 要上传的文件的大小
result 上传许可
3)返回文件列表格式
*; @action=GroupFileList["fileName1":"fileName2":"fileName3"...]
复制代码
服务端:
- 服务端负责处理客户端的各种消息,比如请求下载消息@action=Download["fileName":"fileSize":"result"],把客户端请求下载的文件名解析出来,服务器端判断是否存在该文件,并把结果按请求下载消息把文件大小和下载许可返回客户端,若存在该文件,然后服务器打开对客户端的输出流输出文件,客户端接收到许可消息后,打开对服务器的输入流读取文件。
源码
服务器代码
- 一些文件的保存和读取路径目录,需根据自己项目目录结构更改
public class FileServer {
private final int port = 5203;
private ServerSocket server_socket; //普通消息传输服务器套接字
private ServerSocket fileServerSocket; // 文件传输服务器套接字
private String path ="文件服务器/GroupFile"; //服务器文件保存路径
public FileServer() {
try {
//1-初始化
server_socket = new ServerSocket(this.port); // 创建消息传输服务器
fileServerSocket = new ServerSocket(8888); //创建文件传输服务器
System.out.println("文件服务器启动等待客户端连接.....");
//2-每次接收一个客户端请求连接时都启用一个线程处理
while(true) {
Socket client_socket = server_socket.accept();
System.out.println("客户端:"+client_socket.getRemoteSocketAddress()+"已连接");
new ServerThread(client_socket).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/** -----------------------------------------------------------------------------------------------------
*
* 启用一个线程处理客户端的请求
*/
private class ServerThread extends Thread{
private Socket client_socket;
private BufferedReader server_in;
private PrintWriter server_out;
public ServerThread(Socket client_socket) {
try {
//初始化
this.client_socket = client_socket;
server_in = new BufferedReader(new InputStreamReader(this.client_socket.getInputStream()));
server_out = new PrintWriter(this.client_socket.getOutputStream(),true);
} catch (IOException e) {
}
}
public void run() {
try {
String uploadFileName = null;
String uploadFileSize = null;
String fromClientData ;
while((fromClientData = server_in.readLine()) != null){
//把服务器文件列表返回
if(fromClientData.startsWith("@action=loadFileList")){
File dir = new File(path);
if (dir.isDirectory()){
String[] list = dir.list();
String filelist = "@action=GroupFileList[";
for (int i = 0; i < list.length; i++) {
if (i == list.length-1){
filelist = filelist + list[i]+"]";
}else {
filelist = filelist + list[i]+":";
}
}
server_out.println(filelist);
}
}
//请求上传文件
if (fromClientData.startsWith("@action=Upload")){
uploadFileName = ParseDataUtil.getUploadFileName(fromClientData);
uploadFileSize = ParseDataUtil.getUploadFileSize(fromClientData);
File file = new File(path,uploadFileName);
//文件是否已存在
if (file.exists()){
//文件已经存在无需上传
server_out.println("@action=Upload[null:null:NO]");
}else {
//通知客户端开可以上传文件
server_out.println("@action=Upload["+uploadFileName+":"+uploadFileSize+":YES]");
//开启新线程上传文件
new HandleFileThread(1,uploadFileName,uploadFileSize).start();
}
}
//请求下载文件
if(fromClientData.startsWith("@action=Download")){
String fileName = ParseDataUtil.getDownFileName(fromClientData);
File file = new File(path,fileName);
if(!file.exists()){
server_out.println("@action=Download[null:null:文件不存在]");
}else {
//通知客户端开可以下载文件
server_out.println("@action=Download["+file.getName()+":"+file.length()+":OK]");
//开启新线程处理下载文件
new HandleFileThread(0,file.getName(),file.length()+"").start();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 文件传输线程
*/
class HandleFileThread extends Thread{
private String filename;
private String filesize;
private int mode; //文件传输模式
public HandleFileThread(int mode,String name,String size){
filename = name;
filesize = size;
this.mode = mode;
}
public void run() {
try {
Socket socket = fileServerSocket.accept();
//上传文件模式
if(mode == 1){
//开始接收上传
BufferedInputStream file_in = new BufferedInputStream(socket.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(path,filename)));
int len;
byte[] arr = new byte[8192];
while ((len = file_in.read(arr)) != -1){
bos.write(arr,0,len);
bos.flush();
}
server_out.println("@action=Upload[null:null:上传完成]");
server_out.println("/n");
bos.close();
}
//下载文件模式
if(mode == 0){
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File(path,filename)));
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
System.out.println("服务器:开始下载");
int len;
byte[] arr =new byte[8192];
while((len = bis.read(arr)) != -1){
bos.write(arr,0,len);
bos.flush();
}
socket.shutdownOutput();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//启动程序
public static void main(String[] args) {
new FileServer();
}
}
复制代码
客户端代码
- 文件列表显示图标fileIconPath自行提供设置,亦可删除
public class GroupFileView extends JFrame {
private int width = 400;
private int height = 600;
private JLabel groupLabel;
private JButton uploadButton;
private JLabel flushLabel;
private String fileIconPath = "文件客户端/resource/file-min.png";
private JScrollPane jScrollPane;
private JPanel staffPanel; //在JSpanel上的panel
private Socket client_socket;
private PrintStream client_out;
private BufferedReader client_in;
private String ip = "127.0.0.1";
private int port = 5203;
private File currentUpploadFile;
private String downloadSavePath;
private int Y = 0;
public GroupFileView() {
//1-初始化
initVariable();
//2-连接服务器
connectServer();
//3-注册监听
registerListener();
//4-初始化窗口设置
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(width,height);
this.setTitle("群文件");
this.setLocationRelativeTo(null);//窗口居中显示
this.setResizable(false);
this.setVisible(true);
}
private void initVariable() {
jScrollPane = new JScrollPane();
this.getContentPane().add(jScrollPane);
staffPanel = new JPanel();
///staffPanel.setLayout(new BoxLayout(staffPanel,BoxLayout.Y_AXIS));
staffPanel.setLayout(null);
staffPanel.setOpaque(false);
staffPanel.setPreferredSize(new Dimension(width,height));
jScrollPane.setViewportView(staffPanel);
jScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);//设置水平滚动条隐藏
jScrollPane.getViewport().setOpaque(false); //设置透明
jScrollPane.setOpaque(false); //设置透明
renderTop();
}
/**
* 向服务器重新读取群文件列表
*/
private void loadGroupFile() {
client_out.println("@action=loadFileList");
}
/**
* 渲染顶部面板
*/
private void renderTop(){
staffPanel.removeAll();
Y = 0;
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(1,3,3,10));
this.groupLabel = new JLabel("/t/t/t/t/t群文件列表 ");
this.uploadButton = new JButton("上传文件 ");
flushLabel = new JLabel(new ImageIcon("聊天室客户端/resource/flush.png"));
panel.add(groupLabel);
panel.add(uploadButton);
panel.add(flushLabel);
panel.setBounds(2,Y,width,30);
this.staffPanel.add(panel);
Y += 30;
}
/**
渲染文件列表
*/
public void addToFileList(String filename){
JLabel fileicon = new JLabel(new ImageIcon(fileIconPath));
JButton downloadBtn = new JButton("下载");
JLabel fileNameLab = new JLabel(filename);
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(1,3,0,0));
panel.add(fileicon);
panel.add(fileNameLab);
panel.add(downloadBtn);
//panel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
panel.setBounds(2,Y,width,30);
this.staffPanel.add(panel);
Y+=30;
panel.addMouseListener(new MouseAdapter() {
//鼠标移入时
public void mouseEntered(MouseEvent e) { // 鼠标移动到这里的事件
panel.setBackground(Color.orange);
panel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); // 让鼠标移动到
}
public void mouseExited(MouseEvent e) { // 鼠标离开的事件
panel.setBackground(Color.white);
}
});
//文件下载
downloadBtn.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
//1-选择下载保存的位置
JFileChooser f = new JFileChooser(); // 查找文件
f.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
f.showOpenDialog(null);
File file = f.getSelectedFile();
if(file != null){
downloadSavePath = file.getPath();
//向服务器请求下载
client_out.println("@action=Download["+filename+":null:null]");
}
}
});
}
/**
* 注册监听
*/
private void registerListener() {
//上传文件 消息格式: @action=Upload["fileName":"fileSize":result]
this.uploadButton.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
JFileChooser f = new JFileChooser(); // 查找文件
f.showOpenDialog(null);
currentUpploadFile = f.getSelectedFile();
if(currentUpploadFile != null)
client_out.println("@action=Upload["+currentUpploadFile.getName()+":"+currentUpploadFile.length()+":null]");
}
});
//刷新文件列表按钮
flushLabel.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
loadGroupFile();
}
//鼠标移入时
public void mouseEntered(MouseEvent e) { // 鼠标移动到这里的事件
flushLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); // 让鼠标移动到
}
});
}
/**
* 连接服务器
*/
private void connectServer(){
//连接服务器
try {
//初始化
client_socket = new Socket(ip,port);
client_out = new PrintStream(client_socket.getOutputStream(),true);
client_in = new BufferedReader(new InputStreamReader(client_socket.getInputStream()));
//读取文件列表
client_out.println("@action=loadFileList");
//开启线程监听服务器消息
new ClientThread().start();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 监听服务器消息
*/
class ClientThread extends Thread{
public void run() {
try {
String fromServer_data;
int flag = 0;
while((fromServer_data=client_in.readLine()) != null){
//读取群文件列表
if(flag++ == 0){
if (fromServer_data.startsWith("@action=GroupFileList")){
String[] fileList = ParseDataUtil.getFileList(fromServer_data);
for (String filename : fileList) {
addToFileList(filename);
}
}
continue;
}
if(fromServer_data.startsWith("@action=GroupFileList")){
//重新渲染顶部面板
renderTop();
//注册监听
registerListener();
//渲染文件面板
String[] fileList = ParseDataUtil.getFileList(fromServer_data);
for (String filename : fileList) {
addToFileList(filename);
}
}
//文件上传
if (fromServer_data.startsWith("@action=Upload")){
String res = ParseDataUtil.getUploadResult(fromServer_data);
if("NO".equals(res)){
JOptionPane.showMessageDialog(null,"文件已存在!");
}else if ("YES".equals(res)){
//开始上传
if(currentUpploadFile != null){
//开启新线程传输文件
new HandelFileThread(1).start();
}
}else if ("上传完成".equals(res)){
JOptionPane.showMessageDialog(null,res);
loadGroupFile();
}
}
//文件下载
if(fromServer_data.startsWith("@action=Download")){
String res = ParseDataUtil.getDownResult(fromServer_data);
if(res.equals("文件不存在")){
JOptionPane.showMessageDialog(null,"该文件不存在404");
}else {
String downFileName = ParseDataUtil.getDownFileName(fromServer_data);
String downFileSize = ParseDataUtil.getDownFileSize(fromServer_data);
//开启新线程传输文件
new HandelFileThread(0,downFileName,downFileSize).start();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**----------------------------------------------------------------------------------
* 文件传输线程
*/
class HandelFileThread extends Thread{
private int mode; //文件传输模式 1-上传 2-下载
private String filename;
private Long fileSize;
public HandelFileThread(int mode) {
this.mode = mode;
}
public HandelFileThread(int mode,String filename,String fileSize){
this.mode = mode;
this.filename = filename;
this.fileSize = Long.parseLong(fileSize);
}
public void run() {
try {
//上传文件模式
if(this.mode == 1){
Socket socket = new Socket(ip,8888);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(currentUpploadFile));
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
int len;
int i = 0;
double sum = 0;
byte[] arr = new byte[8192];
String schedule;
System.out.println("开始上传--文件大小为:"+currentUpploadFile.length());
while((len = bis.read(arr)) != -1){
bos.write(arr,0,len);
bos.flush();
sum += len;
if (i++ %100 == 0){
schedule = "上传进度:"+100*sum/currentUpploadFile.length()+"%";
System.out.println(schedule);
}
}
//上传完成
socket.shutdownOutput();
System.out.println("上传进度:100%");
}
//下载文件模式
if(this.mode == 0){
Socket socket = new Socket(ip,8888);
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(downloadSavePath+"/"+filename));
int len;
byte[] arr =new byte[8192];
double sumDown = 0;
int i = 0;
System.out.println("客户端开始下载 ");
while ((len = bis.read(arr)) != -1){
sumDown += len;
if(i++%100 == 0)
System.out.println("下载进度为:"+100*sumDown/fileSize+"%");
bos.write(arr,0,len);
bos.flush();
}
bos.close();
bis.close();
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//启动程序
public static void main(String[] args) throws Exception {
new GroupFileView();
}
复制代码
消息解析工具:
public class ParseDataUtil {
/**
*
* 下载文件消息格式: @action=Download["fileName":"fileSize":"result"]
* 参数说明:
* fileName 要下载的文件的名字
* fileSize 要下载的文件的大小
result 下载许可
*/
public static String getDownFileName(String data){
return data.substring(data.indexOf("[")+1,data.indexOf(":"));
}
public static String getDownFileSize(String data){
return data.substring(data.indexOf(":")+1,data.lastIndexOf(":"));
}
public static String getDownResult(String data){
return data.substring(data.lastIndexOf(":")+1,data.length()-1);
}
/**
*
* 上传文件消息格式: @action=Upload["fileName":"fileSize":result]
参数说明:
* fileName 要上传的文件的名字
* fileSize 要上传的文件的大小
result 上传许可
*/
public static String getUploadFileName(String data){
return data.substring(data.indexOf("[")+1,data.indexOf(":"));
}
public static String getUploadFileSize(String data){
return data.substring(data.indexOf(":")+1,data.lastIndexOf(":"));
}
public static String getUploadResult(String data){
return data.substring(data.lastIndexOf(":")+1,data.length()-1);
}
/** 返回文件列表格式
@action=GroupFileList["fileName":"fileName":"fileName"...]
*/
public static String[] getFileList(String data){
String list = data.substring(data.indexOf("[")+1,data.length()-1);
return list.split(":");
}
}
复制代码
https://juejin.im/post/5d998cd16fb9a04de818e2eb