使用Socket上传文件

使用Socket完成文件上传案例


一、文件上传原理图解
文件上传原理
在这里插入图片描述
二、思路

  • 创建TCPClient的class类

客户端:读取本地文件,上传到服务器,读取服务器回写的数据
数据源:c:\\1.jpg
目的地:服务器

实现步骤:
1.创建一个本地字节输入流FileInputStream,构造方法中要绑定读取的数据
2.创建一个客户端Socket对象,构造方法中绑定服务器的IP地址和端口号
3.使用Socket中的方法getOutputStream,获取网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
4.使用本地字节输出流FilePutStream对象中的方法read,读取本地文件
5.使用字节输出流OutputStream对象中的方法write,把读取到的文件上传到服务器
6.使用Socket中的方法getInputStream,获取网络字节输入流InputStream对象
7.使用网络字节输入流InputStream对象中的方法read读取服务器回写的数据
8.释放资源(FileInputStream,Socket)

  • 创建TCPServer的class类

服务器端:读取客户端上传的文件,保存到服务器的硬盘,给客户端回写“上传成功”
数据源:客户端上传的文件
目的地:服务器的硬盘 d:\\upload\\1.jpg

实现步骤:
1.创建一个服务器ServerSocket对象,获取系统要指定的端口号
2.使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象
3.使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
4.判断d:\upload文件夹是否存在,不存在则创建
5.创建一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
6.使用字节输入流InputStream对象中的方法read,读取客户端上传的文件
7.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
8.使用网络字节输出流OutputStream对象中的方法write,给客户端回写“上传成功”
9.释放资源(FileOutputStream,Socket,ServerSocket)

三、优化分析

1.客户端、服务器进入死循环无法终止问题:

由于代码中使用的while循环,fis.read(bytes)读取本地文件,结束标记读取到-1结束,但是while循环不能读取到-1,那么也不会把结束标记写给服务器。is.read读取不到服务器回写的数据,进入到阻塞状态。

Solution:使用shut downOutput():对于TCP套接字。

任何以前写入的数据都将被发送,并且后跟TCP的正常连接终止序列。如果在套接字上调用shut downOutput()后写入套接字输出流,则该将抛出IOException。
socket.shutdownOutput //放在客户端的while循环之后

2.上传的文件名称写死问题:

如果保存文件的名称写死,那么服务器硬盘只能保留一个文件。

Solution:使用系统时间优化+随机数保证文件命名唯一。

String fileName = "MyPhoto"+System.currentTimeMillis()+/*new Random().nextInt(99999)+*/".jpg";

3.循环接受问题:
服务器也不能只保存一个文件就关闭名之后的用户无法上传,这是不符合实际的

Solution:使用循环改进,可以不断的接受不用用户的文件,让服务器一直处于监听状态。服务器就不用关闭了。

while (true){
	Socket socket = server.accept();
	...
}

4.效率问题:
服务器端在接受大文件时,可能耗费几秒钟的时间,此时不能接受其他用户上传。

Solution:使用多线程技术优化

new Thread(new Runnable() {
                //注:重写run方法完成文件的上传 ,但并不能重写throws IOException的异常,可以手动try...catch解决
                @Override
                public void run() {
	...
}).start();

四、改进之后的代码

1.TCPClient

package UpdateFileUpload;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class TCPClient {
    public static void main(String[] args) throws IOException {
        //1.创建一个本地字节输入流FileInputStream,构造方法中要绑定读取的数据
        FileInputStream fis = new FileInputStream("d:\\1.jpg");
        //2.创建一个客户端Socket对象,构造方法中绑定服务器的IP地址和端口号
        Socket socket = new Socket("127.0.0.1",8888);
        //3.使用Socket中的方法getOutputStream,获取网络字节输出流OutputStream对象
        OutputStream os = socket.getOutputStream();

        //4.使用本地字节输出流FilePutStream对象中的方法read,读取本地文件
        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len = fis.read(bytes)) != -1) {
            //5.使用字节输出流OutputStream对象中的方法write,把读取到的文件上传到服务器
            os.write(bytes,0,len);
        }

        /*
            解决由while发生的阻塞问题:上传完文件,给服务器一个结束标记
            void shutdownOutput 禁止此套接字的输出流
            对于TCP套接字,任何以前写入的数据都将被发送,并且以后跟TCP的正常连接终止序列。
         */
        socket.shutdownOutput();

        //6.使用Socket中的方法getInputStream,获取网络字节输入流InputStream对象
        InputStream is = socket.getInputStream();

        //7.使用网络字节输入流InputStream对象中的方法read读取服务器回写的数据
        while ((len = is.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }
        //8.释放资源(FileInputStream,Socket)
        fis.close();
        socket.close();

    }
}

2.ServerClient

package UpdateFileUpload;
/*
    文件上传案例的服务器端:读取客户端上传的文件,保存到服务器的硬盘,给客户端回写上传成功
    明确:
        数据源:客户端上传的文件
        目的地:服务器的硬盘:d:\\upload\\1.jpg

 */

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;

public class TCPServer {
    public static void main(String[] args) throws IOException {
        //1.创建一个服务器ServerSocket对象,获取系统要指定的端口号
        ServerSocket server = new ServerSocket(8888);
        //2.使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象

        /*
            让服务器一直处于监听状态(死循环accept方法)
            有一个客户端上传文件,就保存一个文件
         */
        while (true){

            /*
                使用多线程技术提高程序的效率(多人同时上传)
                有一个客户端上传文件那就开启一个线程,完成文件的上传
             */
            new Thread(new Runnable() {
                //重写run方法完成文件的上传 ,但自雷重写异常也不能声明抛出IOException异常,可以用try...catch
                @Override
                public void run() {
                    try{
                        Socket socket = server.accept();

                        //3.使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
                        InputStream is = socket.getInputStream();
                        //4.判断d:\\upload文件夹是否存在,不存在则创建
                        File file = new File("d:\\upload");
                        if (!file.exists()){
                            file.mkdirs();
                        }
                    /*
                        自定义一个文件命名规则:防止同名的文件被覆盖
                        规则:域名+毫秒值+随机数
                     */
                        String fileName = "itcast"+System.currentTimeMillis()+/*new Random().nextInt(99999)+*/".jpg";

                        //5.创建一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
                        FileOutputStream fos = new FileOutputStream(file+"\\"+fileName);
                        //6.使用字节输入流InputStream对象中的方法read,读取客户端上传的文件
                        int len = 0;
                        byte[] bytes = new byte[1024];
                        while ((len = is.read()) != -1){
                            fos.write(bytes,0,len);
                        }
                        //8.使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
                        //9.使用网络字节输出流OutputStream对象中的方法write,给接护短回写“上传成功”

                        socket.getOutputStream().write("上传成功".getBytes());
                        //10.释放资源(FileOutputStream,Socket,ServerSocket)
                        fos.close(); //可以放在finally
                        socket.close();
                    }catch (IOException e){
                        System.out.println(e);
                    }
                }
            }).start();

        }

        //服务器就不用关闭了
        //server.close();

    }
}
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
什么是HPSocket HP-Socket 是一套通用的高性能 TCP/UDP 通信框架,包含服务端组件、客户端组件和Agent组件,广泛适用于各种不同应用场景的 TCP/UDP 通信系统,提供 C/C++、C#、Delphi、E(易语言)、Java、Python 等编程语言接口。HP-Socket 对通信层实现完全封装,应用程序不必关注通信层的任何细节;HP-Socket 提供基于事件通知模型的 API 接口,能非常简单高效地整合到新旧应用程序中[1]。 为了让使用者能方便快速地学习和使用 HP-Socket ,迅速掌握框架的设计思想和使用方法,特此精心制作了大量 Demo 示例(如:PUSH 模型示例、PULL 模型示例、性能测试示例以及其它编程语言示例)。HP-Socket 目前运行在 Windows 平台,将来会实现跨平台支持。 --- 百度百科 什么是异步, 什么是同步 "HPsocket所有组件都是异步的"我记得我开始学习HP的时候看见这行字我内心是崩溃的. 我觉得一些聊天室/游戏什么的才用异步,那么我专门写一些网关中间件什么的. 常用的是"应答式(同步)".即: 发送响应.类似http请求, 但不会断开客户端 异步: 专门绑定一个事件, 收到的所有数据都投送给事件处理.适合聊天室/游戏等等 同步: 发送后等待响应, 获取响应数据后继续向下执行. 不需要绑定事件什么的.类似post, 不会断开连接就是了 为什么做这个玩意 记得是去年开始学习hp的, 当时在群"怪物乐园"询问如何实现同步. 与某(忘记了)个管理发生了py交易. 他热心的帮助我写了一个"PACK模型"的同步操作例子. 之后应用在一个项目中并不理想,因为PACK模型是整个包发送,有大小限制,还经常出现问题. 今年一个项目想用HP的PULL模型,所以自己分析那个大神给的例子.终于魔改封装出了一个"PULL模型"的同步操作类. 之前有易友希望我开源,其实我早就想开源了的,因为需要从项目中分离出来太麻烦了, 而且工作忙(上班),所以就拖到了今天

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值