一、原理
【客户端】读取本地的文件 将文件上传到服务器
【服务器】再把上传的文件保存到服务器的硬盘上
1、客户端使用【本地字节输入流】 读取要上传的文件
2、客户端使用(网络字节输出流) 将读取到的文件上传到服务器
3、服务器使用(网络字节输入流) 读取客户端上传的文件
4、服务器使用【本地字节输出流】 将读取到的文件保存在服务器的硬盘上
5、服务器使用(网络字节输出流) 给客户端回写"上传成功"
6、客户端使用(网络字节输入流) 读取服务器回写的数据
7、释放数据
客户端和服务器对本地硬盘进行读写 使用自己创建的字节流对象(即本地流)
客户端和服务器二者之间进行读写 使用Socket中提供的字节流对象(即网络流)
二、文件上传案例
1、服务端
// 1、创建一个服务器ServiceSocket对象 向系统要指定的端口号
ServerSocket serverSocket=new ServerSocket(8888);
// 2、使用ServerSocket对象中的accept()方法 获取请求的客户端Socket对象
Socket socket=serverSocket.accept();
// 3、使用Socket对象中的getInputStream()方法 获取到网络字节输入流InputStream对象
InputStream inputStream=socket.getInputStream();
// 4、判断E:\\upload文件夹(目标文件夹)是否存在 若不存在则创建
File file=new File("E:\\upload");
if (!file.exists())
{
// mkdirs()方法:可建立多级文件夹
file.mkdirs();
}
// 5、创建一个本地字节输出流FileOutputStream对象 构造方法中绑定要输出的目的地
FileOutputStream fileOutputStream=new FileOutputStream(file+"\\test.jpg");
// 6、使用网络字节输入流InputStream对象中的read()方法 读取客户端上传的文件
int len=0;
byte[] bytes=new byte[1024];
while ((len=inputStream.read(bytes))!=-1)
{
// 7、使用本地字节输出流FileOutStream对象中的write()方法 将读取到的文件上传到服务器的硬盘上
fileOutputStream.write(bytes,0,len);
}
// 8、使用Socket对象中的getOutputStream()方法 获取网络字节输出流OutputStream对象
// 9、使用网络字节输出流OutputStream对象中的write()方法 给客户端回写"上传成功"语句
socket.getOutputStream().write("上传成功".getBytes());
// 10、释放资源
fileOutputStream.close();
socket.close();
serverSocket.close();
2、客户端
// 1、创建一个本地字节输出流FileInputStream对象 构造方法中绑定要读取的数据源
FileInputStream fileInputStream=new FileInputStream("D:\\test.jpg");
// 2、创建一个客户端Socket对象 构造方法中绑定服务器的IP地址和端口号
Socket socket=new Socket("192.168.36.56",8888);
// 3、使用Socket中的方法getOutputStream 获取网络字节输出流OutputStream对象
OutputStream outputStream=socket.getOutputStream();
// 4、使用本地字节输入流FileInputStre对象中的read()方法 读取本地的要上传的文件
int len=0;
byte[] bytes=new byte[1024];
while ((len=fileInputStream.read(bytes))!=-1)
{
// 5、使用网络字节输出流OutputStream对象中的write()方法 将读取到的文件上传到服务器
outputStream.write(bytes,0,len);
}
// 6、解决阻塞问题:上传完文件给服务器写一个结束标记
// void shutdownOutput() 禁用此套接字的输出流
// 对于TCP套接字 任何以前写入的数据都将被发送 并且之后会跟TCP的正常连接终止序列
socket.shutdownOutput();
// 7、使用Socket中的getInputStream()方法 读取网络字节输入流InputStream对象
InputStream inputStream=socket.getInputStream();
while ((len=inputStream.read(bytes))!=-1)
{
System.out.println(new String(bytes,0,len));
}
// 8、释放资源
fileInputStream.close();
socket.close();
三、优化
1、文件名定死 再上传文件时原文件被覆盖的问题
自定义文件命名规则 防止同名文件被覆盖
规则:域名+毫秒值+随机数
String fileName="zjitc"+System.currentTimeMillis()+new Random().nextInt(99999)+".jpg";
FileOutputStream fileOutputStream=new FileOutputStream(file+"\\"+fileName);
2、上传一个文件就无法继续上传的问题
使服务器一直处于监听窗台(死循环accept()方法)
只要有一个客户端上传文件 就保存一个文件
while (true)
{
// 使用ServerSocket对象中的accept()方法 获取请求的客户端Socket对象
Socket socket=serverSocket.accept();
......
// 释放资源
fileOutputStream.close();
socket.close();
}
// 当服务器一直处于监听状态 此时 服务器就无需关闭了
// serverSocket.close();
3、上传速度过慢 效率过低的问题
使用多线程技术 提高程序效率
每当有一个客户端上传文件 就开启一个线程 以完成文件的上传
但由于实现Runnable接口 里面的代码无法抛出异常 因此只能try catch
while (true)
{
Socket socket=serverSocket.accept();
new Thread(new Runnable() {
@Override
public void run() {
try {
// 使用ServerSocket对象中的accept()方法 获取请求的客户端Socket对象
Socket socket=serverSocket.accept();
......
// 释放资源
fileOutputStream.close();
socket.close();
}
catch (IOException e)
{
System.out.println(e);
}
}
}).start();
}