上传文件-课后程序(JAVA基础案例教程-黑马程序员编著-第九章-课后作业)

news2024/9/30 7:30:56

【案例9-3】  客户端向服务端上传文件

 【案例介绍】

 1.案例描述

编写一个客户端向服务端上传文件的程序,要求使用TCP通信的的知识,将本地机器输入的路径下的文件上传到D盘中名称为upload的文件夹中。并把客户端的IP地址加上count标识作为上传后文件的文件名,即IP(count)的形式。其中,count随着文件的增多而增大,例如127.0.0.(1).jpg、127.0.0.(2).jpg。

  2.效果显示

上传文件之前

上传文件之后

 

【案例分析】

(1)根据任务描述中使用TCP通信的知识实现文件上传功能可知,要实现此功能,需要定义一个服务器接收文件的程序和 一个客户端上传文件的程序。

(2)首先要编写服务器端程序来接收文件。服务器端需要使用ServerSocket对象的accept()方法接收客户端的请求,由于一个服务器可能对于多个客户端,所以当客户端与服务器端简历连接后,服务器需要单独开启一个新的线程来处理与客户端的交互,这时需要在服务器端编写开启新线程的方法。在新线程的方法中,需要获取客户端的端口号,并且使用输入输出流来传输文件到指定的目录中。

(3)编写客户端的功能代码,客户端功能的实现,因为是用户自己输入上传文件。所以要定义键盘录入。录入后需要使用Socket类来创建客户对象,并通过输入输出流来定义指定的文件。

(4)最后我们启动程序,先启动服务端程序,再运行客户端程序来测试上传的结果。

【案例实现】

(1)首先编写服务器端的程序,用来接收文件,其代码具体如下所示。

FileServer.java

  1. package chapter0903;
  2. import java.io.File;
  3. import java.io.FileOutputStream;
  4. import java.io.InputStream;
  5. import java.io.OutputStream;
  6. import java.net.ServerSocket;
  7. import java.net.Socket;
  8. public class FileServer {
  9.     public static void main(String[] args) throws Exception {
  10.          //创建ServerSocket对象
  11.         ServerSocket serverSocket = new ServerSocket(10001);      
  12.          while (true) {
  13.            // 调用accept()方法接收客户端请求,得到Socket对象
  14.            Socket s = serverSocket.accept();
  15.            // 每当和客户端建立Socket连接后,单独开启一个线程处理和客户端的交互
  16.            new Thread(new ServerThread(s)).start();
  17.         }
  18.     }
  19. }
  20. class ServerThread implements Runnable {
  21.     // 持有一个Socket类型的属性
  22.     private Socket socket;
  23.     // 构造方法中把Socket对象作为实参传入
  24.     public ServerThread(Socket socket) {
  25.         this.socket = socket;
  26.     }
  27.     public void run() {
  28.         // 获取客户端的IP地址
  29.         String ip = socket.getInetAddress().getHostAddress();
  30.         // 上传图片个数
  31.         int count = 1;
  32.         try {
  33.            InputStream in = socket.getInputStream();
  34.            // 创建上传图片目录的File对象
  35.            File parentFile = new File("D:\\upload\\");
  36.            // 如果不存在,就创建这个目录
  37.             if (!parentFile.exists()) {
  38.                parentFile.mkdir();
  39.            }
  40.            // 把客户端的IP地址作为上传文件的文件名
  41.            File file = new File(parentFile, ip + "(" + count +
  42.                                                             ").jpg");
  43.            while (file.exists()) {
  44.                // 如果文件名存在,则把count++
  45.                file = new File(parentFile, ip + "(" + (count++) +
  46.                                                             ").jpg");
  47.            }
  48.            // 创建FileOutputStream对象
  49.            FileOutputStream fos = new FileOutputStream(file);
  50.            // 定义一个字节数组
  51.            byte[] buf = new byte[1024];
  52.            // 定义一个int类型的变量len,初始值为0
  53.            int len = 0;
  54.            // 循环读取数据
  55.            while ((len = in.read(buf)) != -1) {
  56.                fos.write(buf, 0, len);
  57.            }
  58.            // 获取服务端的输出流
  59.            OutputStream out = socket.getOutputStream();
  60.            // 上传成功后向客户端写出“上传成功”
  61.            out.write("上传成功".getBytes());
  62.            // 关闭输出流对象
  63.            fos.close();
  64.            // 关闭Socket对象
  65.            socket.close();
  66.         } catch (Exception e) {
  67.            throw new RuntimeException(e);
  68.         }
  69.     }
  70. }

运行结果如图所示。

服务器端运行结果

上述代码中,第10行代码用于创建一个ServerSocket对象,第11~16行代码用于在while(true)无限循环中调用ServerSocket的accept()方法来接收客户端的请求,没当和一个客户端建立Socket连接后,就开启一个新的线程和这个客户端进行交互,开启的新线程是通过实现Runnable接口创建的,重写的run()方法中实现了服务端接收并保存客户端上传文件的功能在第34行代码上对文件的保存目录用一个File对象进行封装,如果这个目录不存在就调用File的mkdir()方法创建这个目录,为了避免存放的图片名重复而导致的新上传的文件将已经存在的文件覆盖,在第30行代码定义了一个整型变量count,用于统计文件的数目,使用“IP地址(count).jpg”作为上传文件的名称。在第42~46行代码用于对表示文件名的File对象进行循环判断,如果文件名存在则一直执行count++。最后将从客户端接收的文件信息写入到指定的目录中,在第58~60行代码用于获取服务端的输出流,向客户端输出“上传成功”信息。通过图中运行结果可以看出,服务器端进入阻塞状态,等待客户端连接。

(2)创建客户端用于上传文件,其代码如下所示。

FileClient.java

  1. package chapter0903;
  2. import java.io.FileInputStream;
  3. import java.io.InputStream;
  4. import java.io.OutputStream;
  5. import java.net.Socket;
  6. import java.util.Scanner;
  7. public class FileClient {
  8.     public static void main(String[] args) throws Exception {
  9.         // 创建客户端Socket
  10.         Socket socket = new Socket("127.0.0.1", 10001);
  11.         // 获取Socket的输出流对象
  12.         OutputStream out = socket.getOutputStream();
  13.         // 创建FileInputStream对象
  14.         System.out.println("请输入你要上传文件的路径:");
  15.         Scanner sc =new Scanner(System.in);
  16.         String upload = sc.nextLine();
  17.         if(!upload.isEmpty()){
  18.         FileInputStream fis = new FileInputStream(upload);
  19.         // 定义一个字节数组
  20.         byte[] buf = new byte[1024];
  21.         // 定义一个int类型的变量len
  22.         int len;
  23.         // 循环读取数据
  24.         while ((len = fis.read(buf)) != -1) {
  25.            out.write(buf, 0, len);
  26.         }
  27.         // 关闭客户端输出流
  28.         socket.shutdownOutput();
  29.         // 获取Socket的输入流对象
  30.         InputStream in = socket.getInputStream();
  31.         // 定义一个字节数组
  32.         byte[] bufMsg = new byte[1024];
  33.         // 接收服务端的信息
  34.         int num = in.read(bufMsg);
  35.         String Msg = new String(bufMsg, 0, num);
  36.         System.out.println(Msg);
  37.         // 关键输入流对象
  38.         fis.close();
  39.         // 关闭Socket对象
  40.         socket.close();
  41.         }else {
  42.            System.out.println("对不起请您输入文件路径后再上传!!!");
  43.         }
  44.     }
  45. }

客户端运行结果如图所示。

客户端运行结果

上述代码中,第9行代码用于创建一个Socket对象,指定连接服务器的IP地址和端口号,然后获取Socket的输出流对象。第17~25行代码用于创建FileInputStream对象读取我们键盘录入的文件名称,并通过Socket的输出流对象向服务端发送文件。发送完毕后调用Socket的shutDownOutput()方法关闭客户端的输出流。需要注意的是shutDownOutput()方法非常重要,因为服务器端程序在while循环中读取客户端发送的数据时,如果读取不到数据,fis.read(buf)方法会返回-1。也就是说,只要返回的值不是-1。就说明还有数据,需要一直读取,只有fis.read(buf)方法返回的值是-1时循环才会结束。如果客户端不调用shutDownOutput()方法关闭输出流,服务端的fis.read(buf)方法就不会返回-1,而会一直执行while循环,同时客户端读取服务端数据的read(byte [])方法也是一个阻塞方法,这样服务端和客户端程序就进入了一个“死锁”状态。两个程序都不能结束。客户端上传图片成功后,会读取服务端发送的“上传成功”信息,至此,客户端的程序编写完成。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/381043.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络基础之IP地址和子网掩码

一、IP地址IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。习惯上,我们用分成四段的十进制数表示IP地址,从0.0.0.0 一直到255.255.255.255。互联网上的…

虚拟内存原理

局部性原理 计算机组成原理里我们可以知道cache掉入的数据都是连续的 我们可以看下面的例子,data创建的数组,因为我们要读入的是这个数组所以调入的是这一段内存的内容就大概率不会miss 但是我们要知道有些程序的代码被执行的几率是很小的,…

通达信KDJ抄底指标公式,利用J值小于0

前几天介绍了平滑KDJ指标公式,当时有人提出来不建议处理KDJ,KDJ的特点是灵敏,经过处理后就失去其意义了。不过我认为每种指标有相应的使用场景,不必拘泥于原始指标,指标也是人想出来的。今天就利用KDJ的灵敏&#xff0…

Windows下curl编译,使用vcpkg定制自己的【curl】。

本篇介绍在Windows下如何编译curl,curl自称是星球上最好用的计算机网络工具,但是它在windows上纯手动编译很困难,我们使用vcpkg来简化它的编译,方便我们使用。 目录 一、CURL介绍 二、vcpkg下载、编译、定制【curl】 三、编写…

代码随想录算法训练营第十五天 | 层序遍历 、226.翻转二叉树、101.对称二叉树

打卡第15天,今天继续二叉树 今日任务 层序遍历10道题226.翻转二叉树101.对称二叉树 层序遍历10道题 题单 102.二叉树的层序遍历107.二叉树的层次遍历II199.二叉树的右视图637.二叉树的层平均值429.N叉树的层序遍历515.在每个树行中找最大值116.填充每个节点的下一个…

工作篇:触摸屏原理介绍

一、触摸屏概述 触摸屏作为一种新的输入设备,它是目前最简单、方便、自然的一种人机交互方式。 当接触了屏幕上的图形按钮时,屏幕上的触觉反馈系统可根据预先编程的程式驱动各种连结装置,可用以取代机械式的按钮面板,并借由液晶…

《PMBOK 指南第七版》初识

个人理解: 体系构建变化非常大,7版延续6版的内容,但对项目管理的视角完全不同,系统化的思考方式10知识领域 到 12管理原则的转变,突出了对变化的敏捷应对,体现管理的有效性5过程域 到 8 绩效域的转变&…

Android源码分析 - InputManagerService与触摸事件

0. 前言 有人问到:“通过TouchEvent,你可以获得到当前的触点,它更新的频率和屏幕刷新的频率一样吗?”。听到这个问题的时候我感到很诧异,我们知道Android是事件驱动机制的设计,可以从多种服务中通过IPC通信…

03 Android基础--fragment

03 Android基础--fragment什么是fragment?fragment生命周期?动态的fragment与静态的fragmentfragment常用的两个类与APIFragment与Activity通信什么是fragment? 碎片,一个activity中可以使用多个fragment,可以把activi…

应用模型开发指南上新介绍

Module、HAP、Ability、AbilitySta-ge、Context……您是否曾经被这些搞不懂又绕不开的知识点困扰? 现在,全新的《应用程序包基础知识》及《应用模型开发指南》为您答疑解惑! 这里有您关注的概念解析、原理机制阐述,也有丰富的…

gitlab+idea回退代码并提交到新分支

目录结构前言idea创建新分支查看代码提交记录使用IntelliJ IDEA获取使用Git Bash Here获取代码回退到指定版本回退执行命令行使用IntelliJ IDEA实现使用Git Bash Here实现回退完成验证idea提交指定版本代码验证分支代码推动成功前言 IntelliJ IDEA GitLab开发过程中需将代码回…

ajax调用restful接口

HTTP动词对应操作POST新增信息GET获取信息PUT更新信息DELETE删除信息一、POST-----新增信息 1. 后台接口 PostMapping(value "/save") public String save(RequestBody(required true) Emp emp){System.err.println(emp.toString());// 将数据信息存库empService.…

Android开发面试【金三】——启动优化

前言 一下子来到了,面试的高潮季。金三银四的三月份;在我们Android开发的众多面试中,扑面而来的超多面试题难道很多程序员。 Android的性能优化,主要是从以下几个方面进行优化的: 稳定(内存溢出、崩溃&am…

安全认证--JWT介绍及使用

安全认证--JWT介绍及使用1.无状态登录原理1.1.什么是有状态?1.2.什么是无状态1.3.如何实现无状态1.4.JWT1.4.1.简介1.4.2.数据格式2.编写JWT工具2.1.添加JWT依赖2.2.载荷对象2.3.工具2.4.测试2.4.1.配置秘钥2.4.2.测试类2.5项目源码1.无状态登录原理 有状态登录和无…

G1D54-CRF

一、CRF的输入X是什么?是构造的特征吗? 如此,CRF的x只用于状态函数吗? CRF的例子解释调用代码 机器之心 知乎忆榛 此处线性链条件随机场的特征函数形式被统一了? BilstmCRF,强烈推荐!&#x…

AM402和SV660N、IS620N运动控制

软件:InoProShop(V1.7.3) 1、添加EtherCAT伺服从站 2、PLC运动控制程序和ETHERCAT在一个任务中。 3、编码器脉冲设置。 注意电机转速值是以秒还是分钟计量单位。 SV660N IS620N 4、设置电机停机方式。使用sin停机效果比较圆滑,默认梯形。 5、库管理器…

广和通携手联发科技正式发布基于MediaTek T830 平台5G模组FG370的可快速落地FWA解决方案

2月28日,全球领先的物联网无线通信解决方案和无线通信模组提供商广和通正式宣布:新一代5G模组FG370已率先实现量产,并于2023世界移动通信大会(MWC Barcelona 2023)期间携手联发科技正式发布基于FG370的FWA解决方案&…

十三、MyBatis的缓存

缓存:cache 缓存的作用:通过减少IO的方式,来提高程序的执行效率。 mybatis的缓存:将select语句的查询结果放到缓存(内存)当中,下一次还是这条select语句的话,直接从缓存中取&#xf…

数字信号复习题纲

数字信号复习题纲一、希尔伯特变换器(:heavy_check_mark: )1. 什么是希尔伯特变换器?2. 试证明信号通过希尔伯特变换器后的输出二、能量信号的自相关函数、卷积运算与能量谱(:heavy_check_mark:)1. 能量信号2. 试证明自…

webpack配置完全指南

前言 对于入门选手来讲,webpack 配置项很多很重,如何快速配置一个可用于线上环境的 webpack 就是一件值得思考的事情。其实熟悉 webpack 之后会发现很简单,基础的配置可以分为以下几个方面: entry 、 output 、 mode 、 resolve …