文件上传的原理:
使用客户端读取本地的文件,把文件上传到服务器,服务器再把上传的文件保存到服务器的硬盘上
1.客户端使用本地的字节输入流,读取要上传的文件
2.客户端使用网络字节输出流,把读取到的文件上传到服务器
3.服务器使用网络字节输入流,读取客户端上传的文件
4.服务器使用本地字节输出流,把读取到的文件,保存到服务器的硬盘上
5.服务器使用网络字节输出流,给客户端回写一个“上传成功”
6.客户端使用网络字节输入流,读取服务器回写的数据
7.释放资源
注意:客户端和服务器和本地硬盘进行读写,需要使用自己创建的字节流对象(本地流)
客户端和服务器之间进行读写,必须使用Socket中提供的字节流对象(网络流)
文件上传的原理,就是文件的复制
明确:数据源,数据目的地
文件上传案例的客户端:
/* * 文件上传案例的客户端: * 读取本地文件,上传到服务器,读服务器回写的数据 * 明确:数据源:d:\\1.jpg * 目的地:服务器 * 实现步骤: * 1.创建一个本地的字节输入流FileInputStream对象,构造方法绑定要读取的数据源 * 2.创建一个客户端Socket对象,构造方法中绑定服务器的IP地址和端口号 * 3.使用Socket中的方法getOutputStream,获取网络字节输出流OutputStream对象 * 4.使用本地字节输入流FileInputStream对象中的方法read,读取本地文件 * 5.使用网络字节输出流OutputStream对象中的方法write,把读取到的文件上传到服务器 * 6.使用Socket中的方法getInputStream,获取网络字节输入流InputStream对象 * 7.使用网络字节输入流InputStream对象中的方法read读取服务回写的数据 * 8.释放资源(FileInputStream,Socket)*/ public class TCPClient { public static void main(String[] args) throws IOException { FileInputStream fis=new FileInputStream("d:\\1.jpg"); Socket socket=new Socket("127.0.0.1",8888); OutputStream os = socket.getOutputStream(); int len=0; byte[] bytes=new byte[1024]; while((len=fis.read(bytes))!=-1){ os.write(bytes,0,len); } InputStream is = socket.getInputStream(); while((len=is.read(bytes))!=-1){ System.out.println(new String (bytes,0,len)); } fis.close(); socket.close(); } }
文件上传案例的服务器端:
/* * 文件上传案例的服务器端:读取客户端上传的文件,保存到服务器的硬盘,给客户端回写“上传成功” * 明确: * 数据源:客户端上传的文件 * 目的地:服务器的硬盘 D:\\upload\\1.jpg * 实现步骤: * 1.创建一个服务器ServerSocket对象,和系统要指定的端口号 * 2.使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象 * 3.使用Socket对象中的方法getInputStream,获取网络输入流InputStream对象 * 4.判断D:\\upload文件夹是否存在,不存在则创建 * 5.创建一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地 * 6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件 * 7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上 * 8.使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象 * 9.使用网络字节输出流OutputStream对象中的方法,给客户端回写“上传成功” * 10.释放资源(FileOutputStream,Socket,ServerSocket)*/ public class TCPServer { public static void main(String[] args) throws IOException { ServerSocket server=new ServerSocket(8888); Socket socket = server.accept(); InputStream is = socket.getInputStream(); File file=new File("D:\\upload"); if(!file.exists()){ file.mkdirs(); } FileOutputStream fos=new FileOutputStream(file+"\\1.jpg"); int len=0; byte[] bytes=new byte[1024]; while((len=is.read(bytes))!=-1){ fos.write(bytes,0,len); } socket.getOutputStream().write("上传成功".getBytes()); fos.close(); socket.close(); server.close(); } }
文件上传案例阻塞问题:
客户端里:fis.read(bytes)读取本地文件,结束标记是读取到-1结束
while循环里不会读取-1,那么也不好把结束标记写给服务器。
服务器里:is.read()读取客户端上传的文件,永远也读取不到文件的结束标记,read方法进入到阻塞状态,一直死循环等待结束标记。
回写给客户端的信息无法上传成功。
客户端里:is.read()读取不到服务器回写的数据,进入到阻塞状态
解决:上传完文件,给服务器写一个结束标记
* void shutdownOutput() :禁用此套接字的输出流。 */ socket.shutdownOutput();
文件上传案例优化:
优化内容:
1.自定义一个文件的命名规则:防止文件名被覆盖
2.让服务器一直处于监听状态(死循环accept方法)
3.使用多线程技术,提高程序的效率
public class FileUploadServer { public static void main(String[] args) throws IOException { ServerSocket server=new ServerSocket(8888); /* * 让服务器一直处于监听状态(死循环accept方法) * 有一个客户端上传文件,就保存一个文件 * */ while(true){ Socket socket = server.accept(); /* * 使用多线程技术,提高程序的效率 * 有一个客户端上传文件,就开启一个线程,完成文件的上传 * */ new Thread(new Runnable() { @Override public void run() { //完成文件的上传 try{ InputStream is = socket.getInputStream(); File file=new File("D:\\upload"); if(!file.exists()){ file.mkdirs(); } /* * 自定义一个文件的命名规则:防止文件名被覆盖 * 规则:域名+毫秒值+随机数 * */ String fileName="gkx"+System.currentTimeMillis()+new Random().nextInt(99999)+".jpg"; //FileOutputStream fos=new FileOutputStream(file+"\\1.jpg"); FileOutputStream fos=new FileOutputStream(file+"\\"+fileName); int len=0; byte[] bytes=new byte[1024]; while((len=is.read(bytes))!=-1){ fos.write(bytes,0,len); } socket.getOutputStream().write("上传成功".getBytes()); fos.close(); socket.close(); }catch (IOException e){ System.out.println(e); } } }).start(); } } }
模拟B/S服务器分析:
/* * 创建BS版本TCP服务器 * */ public class TCPServer { public static void main(String[] args) throws IOException { //创建一个服务器ServerSocket,和系统要指定的端口号 ServerSocket server=new ServerSocket(8080); //使用accept方法获取到请求的客户端对象(浏览器) Socket socket = server.accept(); //使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象 InputStream is = socket.getInputStream(); //使用网络字节输入流InputStream对象中的方法read读取客户端的请求信息 byte[] bytes=new byte[1024]; int len=0; while((len=is.read(bytes))!=-1){ System.out.println(new String(bytes,0,len)); } } }
客户端是浏览器-》访问服务器-》
读取客户端的请求信息
GET / NET/web/index.html HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Upgrade-Insecure-Requests: 1
服务器要给客户端回写一个信息,回写一个html页面(文件)
我们需要读取html文件,就必须要知道这个文件的地址,而这个地址就是请求信息的第一行GET / NET/web/index.html HTTP/1.1
可以使用BufferedReader中的方法readLine读取一行
new BufferedReader(new InputStream(is)); 把网络字节输入流转换为字符缓冲输入流
GET /NET/web/index.html HTTP/1.1
可以使用String类的方法split(" ")切割字符串,获取中间的部分
数组的arr[1]: /NET/web/index.html
使用String类的方法subString(1),获取html文件的路径:NET/web/index.html
服务器创建一个本地的字节输入流,根据获取到的文件路径,读取html文件
//写入HTTP协议响应头,固定写法
out.write("HTTP/1.1 200 OK\r\n".getBytes());
out.write("Content- Type:text/html\r\n".getBytes());
//必须要写入空行,否则浏览器不解析
out.write("\r\n".getBytes());
服务器端使用网络字节输出流把读取到的文件写入到客户端(浏览器)上显示
模拟B/S服务器代码实现:
/* * 创建BS版本TCP服务器 * */ public class TCPServer { public static void main(String[] args) throws IOException { //创建一个服务器ServerSocket,和系统要指定的端口号 ServerSocket server=new ServerSocket(8080); /* * 浏览器解析服务器回写的html页面,页面中如果有图片,那么浏览器就会单独的开启一个线程,读取服务器的图片 * 我们就得让服务器一直处于监听状态,客户端请求一次服务器就得回写一次 * */ while(true){ //使用accept方法获取到请求的客户端对象(浏览器) Socket socket = server.accept(); new Thread(new Runnable() { @Override public void run() { try{ //使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象 InputStream is = socket.getInputStream(); //使用网络字节输入流InputStream对象中的方法read读取客户端的请求信息 /* byte[] bytes=new byte[1024]; int len=0; while((len=is.read(bytes))!=-1){ System.out.println(new String(bytes,0,len)); }*/ //把is网络字节输入流对象,转换为字节缓冲输入流 BufferedReader br=new BufferedReader(new InputStreamReader(is)); //把客户端请求信息的第一行读出来 String line = br.readLine(); //把读取的信息进行切割,只要中间的部分 String[] arr = line.split(" "); //把路径前面的/去掉,进行截取 String htmlpath = arr[1].substring(1); //创建一个本地字节输入流,构造方法中绑定要读取的html路径 FileInputStream fis=new FileInputStream(htmlpath); //使用Socket中的方法getOutputStream获取网络字节输出流OutputStream对象 OutputStream os = socket.getOutputStream(); //写入HTTP协议响应头,固定写法 os.write("HTTP/1.1 200 OK\r\n".getBytes()); os.write("Content- Type:text/html\r\n".getBytes()); //必须要写入空行,否则浏览器不解析 os.write("\r\n".getBytes()); //一读一写复制文件,把服务器读取的html文件回写到客户端 int len=0; byte[] bytes=new byte[1024]; while((len=fis.read(bytes))!=-1){ os.write(bytes,0,len); } //释放资源 fis.close(); socket.close(); }catch(IOException e){ System.out.println(e); } } }).start(); } } }