Client类:

public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1",10010);
//        网络字节输入流,用来获取从服务器中传过来的数据(字节)
        InputStream in = socket.getInputStream();
//        网络字节输出流,用来获取向服务器中传输的数据
        OutputStream out = socket.getOutputStream();
//         本地输入流,使用转换流,如果服务器传的是字符,则使用转换流将字节转换成字符
        BufferedReader br = new BufferedReader(new InputStreamReader(in));
//        打印流,PrintStream中的write可以发送字节,println可以发送字符。
        PrintStream ps = new PrintStream(out);
//        首先读取文件名字
        File file = new File("C:\\Users\\chenqingxv\\Pictures\\鞠婧祎.jpg");
//        再将文件名字先传给服务器
        ps.println(file.getName());
//        先不着急往下写,暂停接收消息,防止数据的丢失
/*
   为什么不着急往下继续读取本地文件:如果不利用readLine的阻塞特性,则输出流(打印流)就会将文件名字以及文件内容一起存入内存中,关于缓冲流,
   底层会一次性从硬盘读取一定数量一个固定长度的字符数组后再由readLine读取到第一个“\r\n”结束,也就是在服务器端读文件名字时,
   服务器只readLine了一次读取了文件名字,第一个数组中“\r\n”之后的数据就丢失了。之后服务器再用字节流(in.read(bytes))读文件内容的字符数时就有丢失。
   所以在客户端上传文件名字之后,先用一个readLind方法的特性阻塞一下,防止继续读取,待服务器发送“接收完成”的提示后再继续读取文件中内容,就不会在有丢失。
*/
        String information = br.readLine();
        System.out.println(information);
//        使用本地输入流读取文件
        FileInputStream fis = new FileInputStream("C:\\Users\\chenqingxv\\Pictures\\鞠婧祎.jpg");
//        设置缓冲数组
        byte[] bytes = new byte[1024*8];
        int len;
        while ((len = fis.read(bytes))!= -1){
//            使用网络输出流写到服务器中
            ps.write(bytes,0,len);
        }
//        给服务器一个结束标记,告诉服务器,文件已经传输完毕
        socket.shutdownOutput();
//        接收服务器传送过来的提示字符
        String s = br.readLine();
        System.out.println(s);

        fis.close();
        socket.close();
    }

Server类:

public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(10010);
        //线程池的创建
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 5, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(6));
//        服务器循环接收客户端的链接请求
        while (true) {
            Socket socket = serverSocket.accept();
            ThreadFile threadFile = new ThreadFile(socket);
            pool.submit(threadFile);
        }

Runnable继承类:

private Socket socket;

    public ThreadFile(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {

        InputStream in = null;
        OutputStream out = null;
        String name= null;
        FileOutputStream fileOutputStream = null;
        //         本地输入流,使用转换流(转换成字符流)
        try {
            //        网络字节输入流,用来获取从客户端中传过来的数据(字节)
            in = socket.getInputStream();
            //        网络字节输出流,用来获取从客户端中传输的数据
            out = socket.getOutputStream();
            //            接收首先传过来的文件名字
            PrintStream ps = new PrintStream(out);
            BufferedReader br = new BufferedReader(new InputStreamReader(in));
            name = br.readLine();
//            接收名字结束,向客户端发送一个消息
            ps.println("可以开始了");
            //            使用UUID类随机一个名字
//        使用本地输出流向文件中写入数据
            fileOutputStream = new FileOutputStream("day14\\upload\\"+ UUID.randomUUID().toString()+"_"+name);
            byte[] bytes = new byte[1024*8];
            int len;
            while ((len = in.read(bytes))!=-1){
                fileOutputStream.write(bytes,0,len);
            }
            //        传输完成后给客户端一个提示
            ps.println("上传完成");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                socket.close();
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }