http上传文件原理

文件上传下载,说来悲剧。
以前都是用的ADF\OAF\JALOR等框架开发,SE搭好工程,只负责业务逻辑开发,基本没怎么想过为什么。
直到一天,为了防止系统入侵,做文件上传现在过滤拦截,才想到一个问题。我们上传下载的原理到底是什么?一直再copy代码,为什么要这么做哪?

我们用一个servlet去处理一个上传文件,看下代码很简单,基本是从request读取流,然后通过IO写入到指定目录,或者通过一些中间件写入一些分布式文件平台。
那么文件是怎么序列化为流数据分装在HTTP报文中的哪?
其实我们的文件上传怎么都要经历如下链路:
1、浏览器解析文件,打包成数据流。
2、通过管道和服务器连接,通过http传输数据到后台。
3、java程序(也可能是其他程序)做IO处理,做文件写操作。

浏览器上传文件原理

    

enctype:属性规定在发送到服务器之前应该如何对表单数据进行编码。

form表单中enctype属性可以用来控制对表单数据的发送前的如何进行编码,
enctype有三种,分别为:
multipart/form-data不对字符编码,用于发送二进制的文件,其他两种类型不能用于发送文件;

text/plain用于发送纯文本内容,空格转换为 “+” 加号,不对特殊字符进行编码,一般用于email之类的;

application/x-www-form-urlencoded,在发送前会编码所有字符,即在发送到服务器之前,所有字符都会进行编码(空格转换为 “+” 加号,"+"加号转换为空格,特殊符号转换为 ASCII HEX 值)。

其中application/x-www-form-urlencoded为默认类型。

看下文件上传的报文,就这么一个file控件,以CSDN上传头像为例,浏览器会自动把上传文件解析如下:

Request URL: https://me.csdn.net/api/user/uploadBase64Avatar
Request Method: POST
Status Code: 200
Remote Address: 39.96.132.69:443
Referrer Policy: unsafe-url
access-control-allow-credentials: true
access-control-allow-headers: Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,X-Data-Type,X-Requested-With
access-control-allow-methods: GET,PUT,POST,DELETE,OPTIONS
access-control-allow-origin: https://i.csdn.net
content-type: application/json; charset=UTF-8
date: Sun, 13 Oct 2019 09:26:34 GMT
server: openresty
status: 200
Provisional headers are shown
Accept: application/json, text/javascript, /; q=0.01
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryV5ocEPiRQuRh2vcB
Origin: https://i.csdn.net
Referer: https://i.csdn.net/
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
avatar_file: (binary)
a:

最下面是POST请求封装的Form Data,我们会发现浏览器有自动把数据解析成二进制的数据的功能。
具体数据如下:
------WebKitFormBoundaryV5ocEPiRQuRh2vcB
Content-Disposition: form-data; name=“avatar_file”; filename=“avatar_file.png”
Content-Type: image/png

‰PNG


IHDR––<qâ IDATx^ì½”]gy.ü|»ï}z›3]Ó$z±%[–{ƒÁ—Ä6¡òJnˆ —rC
……………………………………………………
------WebKitFormBoundaryV5ocEPiRQuRh2vcB
Content-Disposition: form-data; name=“a”

------WebKitFormBoundaryV5ocEPiRQuRh2vcB–

中间内容省略

二、服务器
1.服务器端程序收到"multipart/form-data"类型的http请求消息
2.读取这个请求消息里面的实体内容
3.解析每个分区的数据
4.从每个分区中解析出描述头和主体内容部分

	@RequestMapping("/uploadFile")
    public void uploadFile(HttpServletRequest request, HttpServletResponse response) {
        ProgressListener progressListener = new ProgressListener() {
            @Override
            public void update(long l, long l1, int i) {

            }
        };
        String savePath = "D:\\faceImage\\";
        File file = new File(savePath);
        if (!file.exists() && file.isDirectory()) {
            System.out.println(savePath + "目录不存在,需要创建");
            file.mkdir();
        }
        try {
            DiskFileItemFactory factory = new DiskFileItemFactory();
            ServletFileUpload upload = new ServletFileUpload(factory);
            upload.setHeaderEncoding("UTF-8");
            if (!ServletFileUpload.isMultipartContent(request)) {
                //
            }
            upload.setProgressListener(progressListener);
            List<FileItem> fileItemList = upload.parseRequest(request);
            fileItemList.forEach(item -> {
                if (item.isFormField()) {
                    //普通的参数对应的读取
                    String name = item.getFieldName();
                    try {
                        String value = item.getString("UTF-8");
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                } else {
                    //如果fileupload中是上传文件
                    String fileName = item.getName();
                    if (fileName == null || fileName.trim().equals("")) {
                        return;
                    }
                    if (fileName.contains("\\")) {
                        fileName = fileName.substring(fileName.lastIndexOf("\\" + 1));
                    }
                    try {
                        InputStream inputStream = item.getInputStream();
                        FileOutputStream fileOutputStream = new FileOutputStream(savePath + "\\" + fileName);
                        byte[] buffer = new byte[1024];
                        int len = 0;
                        while ((len = inputStream.read(buffer)) > 0) {
                            fileOutputStream.write(buffer);
                        }
                        inputStream.close();
                        fileOutputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

你可能感兴趣的:(http相关知识)