利用多线程技术实现多用户可同时上传和下载文件
最近学习了多线程和网络编程中的C/S结构,便把这些知识结合在一起写了这个包含将本地文件上传到服务器端,和从服务器端下载文件保存到本地的案例,想和大家一起交流一下。
涉及内容:File、Socket、ServerSocket、Thread、InputStream、OutputStream等相关知识。
设计亮点:
1.文件名称写死的问题
服务端/客户端,保存文件的名称如果写死,那么最终导致服务器硬盘,只会保留一个文件,所以使用系统时间+文件后缀命名,保证文件名称唯一。
2.循环接收的问题
服务端,只保存一个文件就关闭了,之后的用户无法再上传,这是不符合实际的,使用循环改进,可以不断的接收不同用户的文件。
3.效率问题
服务端,在接收大文件时,可能耗费几秒钟的时间,此时不能接收其他用户上传,所以,使用多线程技术优化。
设计细节及源码:
文件上传:
文件上传分析图解
1. 【客户端】输入流,从硬盘读取文件数据到程序中。
2. 【客户端】输出流,写出文件数据到服务端。
3. 【服务端】输入流,读取文件数据到服务端程序。
4. 【服务端】输出流,写出文件数据到服务器硬盘中。
信息回写分析图解
前四步与基本文件上传一致.
5. 【服务端】获取输出流,回写数据。
6. 【客户端】获取输入流,解析回写数据。
文件下载原理与文件上传刚好相反,这里便不再赘述,直接奉上源码:
客户端:
package cn.homeWork01.demo09C_S;
// 客户端
// 文件上传下载综合案例
// 存在问题:不能连续向服务器写入数据
import java.io.*;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException{
// 调用上传方法
uploadClient("F:\\Test\\chushibiao.txt");
// 调用下载方法
downloadClient();
}
// 文件上传
public static void uploadClient(String path) throws IOException {
Socket socket = new Socket("127.0.0.1",8888);
OutputStream os = socket.getOutputStream();
InputStream is = socket.getInputStream();
FileInputStream fis = new FileInputStream(path);
// 向服务器发送文件
int len = 0;
byte[] bytes = new byte[1024];
while((len=fis.read(bytes))!=-1){
os.write(bytes,0,len);
}
//禁用此套接字的输出流(给服务器写一个结束标记)
socket.shutdownOutput();
// 读取服务器的反馈信息
while ((len=is.read(bytes))!=-1) {
System.out.println(new String(bytes,0,len));
}
// 关闭流和客户端
fis.close();
socket.close();
}
// 文件下载
public static void downloadClient() throws IOException{
Socket socket = new Socket("127.0.0.1",8889);
OutputStream os = socket.getOutputStream();
InputStream is = socket.getInputStream();
// 判断文件夹是否存在
File download = new File("F:\\Test\\download");
if(!download.exists()){
download.mkdirs();
}
// 生成随机文件名
String downname = "\\"+System.currentTimeMillis()+".txt";
FileOutputStream fos = new FileOutputStream(download+downname);
// 读取服务器发送的文件并写入本地
int len = 0;
byte[] bytes = new byte[1024];
while((len=is.read(bytes))!=-1){
fos.write(bytes,0,len);
}
// 向服务器反馈接收成功信息
os.write("接收成功".getBytes());
// 关闭流和客户端
fos.close();
socket.close();
}
}
服务器端:
package cn.homeWork01.demo09C_S;
// 文件上传下载服务器端
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
// 开启上传服务器
uploadServer();
// 开启下载服务器
downloadServer();
}
// 文件上传服务器
public static void uploadServer() throws IOException{
ServerSocket serverSocket = new ServerSocket(8888);
while(true){
Socket socket = serverSocket.accept();
new Thread(new Runnable() {
@Override
public void run() {
FileOutputStream fos = null;
try{
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
// 判断文件夹是否存在
// 此文件夹为上传至服务器的文件夹
File upload = new File("f:\\Server\\upload");
if(!upload.exists()){
upload.mkdirs();
}
// 生成随机文件名
String name = "\\"+System.currentTimeMillis()+".txt";
fos = new FileOutputStream(upload+name);
// 读取上传文件
int len = 0;
byte[] bytes = new byte[1024];
while((len = is.read(bytes))!=-1){
fos.write(bytes,0,len);
}
// 向客户端反馈信息
os.write("上传成功".getBytes());
}catch(Exception e){
e.printStackTrace();
}finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
// 文件下载服务器
public static void downloadServer() throws IOException{
ServerSocket serverSocket = new ServerSocket(8889);
while (true){
Socket socket = serverSocket.accept();
new Thread(new Runnable() {
FileInputStream fis = null;
@Override
public void run() {
try{
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
// 创建下载文件路径
// 此路径为服务器端的下载文件夹及文件名
File pathname = new File("F:\\Server\\download\\b.txt");
fis = new FileInputStream(pathname);
// 将文件发送给客户端
int len=0;
byte[] bytes = new byte[1024];
while((len=fis.read(bytes))!=-1){
os.write(bytes,0,len);
}
// 禁用此套接字输出流
socket.shutdownOutput();
// 接收客户端的反馈信息
while((len=is.read(bytes))!=-1){
System.out.println(new String(bytes,0,len));
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
}
注意:
1.请先启动服务器端,再启动客户端!
2.本案例还未实现客户端与服务器端的连续通信,所以不能在下载程序的客户端传递想要下载的文件名称!
3.请单独使用上传程序或下载程序,否则会抛出异常!
4.服务器端须手动关闭,不然一直处于运行状态!
不足之处:
1.尚未实现客户端和服务器端的连续通信。
2.不能同时使用上传和下载程序。
3.还未设计友好的交互界面,普通人可能无法使用。
4.没有对线程数量进行限制,当开启过多线程,将会导致内存溢出异常(当然本程序还很难满足此项,大家可放心使用!)。
本案例还有很多不足之处,希望大家能够及时纠正。当然更希望能够对您有用!