Carga y descarga de archivos (super detallada)

La carga y descarga de archivos son funciones muy comunes. En muchos sistemas o software, a menudo se utilizan la carga y descarga de archivos. Por ejemplo: QQ avatar, se utiliza la carga. También hay funciones de carga y descarga de archivos adjuntos en el buzón. La carga de materiales adjuntos está aprobada en el sistema OA.

1. Introducción a la carga de archivos (énfasis)

  1. Debe haber una etiqueta de formulario, método = solicitud de publicación
  2. El enctype valor del atributo de la etiqueta de formulario debe ser una forma de datos multipart / valor
  3. Use input type = file en la etiqueta de formulario para agregar el archivo cargado
  4. Escriba el código del servidor (aquí se usa el programa servlet) para recibir y procesar los datos cargados.

encType = multipart / form-data significa que los datos enviados se empalman en forma de múltiples segmentos (un segmento de datos para cada elemento de formulario) y luego se envían al servidor en forma de flujo binario.

upload.jsp:

<body>
    <form action="http://172.25.11.14:8080/09_EL_JSTL/uploadServlet" method="post" enctype="multipart/form-data">
        用户名:<input type="text" name="username" /> <br>
        头像:<input type="file" name="photo" /> <br>
        <input type="submit" value="上传" />
    </form>
</body>

Luego escriba un UploadServlet.java (reescriba el método doPost) y configure la dirección de acceso para uploadServlet (todo omitido aquí).

1.1 Carga de archivos, descripción del protocolo HTTP

Inserte la descripción de la imagen aquí

Content-Type indica el tipo de datos enviados

multipart / form-data representa los datos enviados, que se empalman en forma de múltiples segmentos (un segmento de datos para cada elemento de formulario) y luego se envían al servidor en forma de flujo binario .

El límite representa el separador de cada dato.

---- WebKitFormBoundaryfR6ijBPPqbdkIBkO es generado aleatoriamente cada vez por el navegador. Es el separador de cada dato.

El siguiente es un ejemplo del servidor que no recibe en forma de flujo:

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //以流的形式发给服务器,则只能以流的形式接收
        //以不是流的形式接收为例
        System.out.println(req.getParameter("username"));
        System.out.println(req.getParameter("photo"));
    }

Después de que el navegador cargó el archivo, se generaron dos nulos en la consola de idea.

El siguiente es un ejemplo de recepción de datos en forma de flujo:

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        ServletInputStream inputStream = req.getInputStream();
        //需要创建一个buff缓冲区
        byte[] buffer = new byte[1024000];
        int read = inputStream.read(buffer);
        System.out.println(new String(buffer,0,read));  //从0开始读了 read 个
    }

Producción:

Inserte la descripción de la imagen aquí

El formato del contenido de salida es similar al cuerpo de la solicitud, el código distorsionado en el medio es la información recibida, que se omite en el cuerpo de la solicitud (se convierte en una línea en blanco)

1.2 introducción a la API común commons-fileupload.jar

commons-fileupload.jar necesita depender del paquete commons-io.jar, por lo que tenemos que importar ambos paquetes.

El primer paso es importar dos paquetes jar:
commons-fileupload-1.2.1.jar
commons-io-1.4.jar

En los paquetes commons-fileupload.jar y commons-io.jar, ¿cuáles son las clases más utilizadas?
La clase ServletFileUpload se usa para analizar los datos cargados.
La clase FileItem representa cada elemento de formulario.

boolean ServletFileUpload.isMultipartContent (solicitud HttpServletRequest);
Determine si el formato de datos cargado actualmente es un formato de varias partes.

publicList <FileItem> parseRequest (HttpServletRequestrequest)
analizar los datos cargados

boolean FileItem.isFormField ()
determina si el elemento de formulario actual es un elemento de formulario normal. El tipo de archivo cargado es
verdadero, lo que significa que el tipo normal de elemento de formulario es
falso, lo que significa que el tipo de archivo cargado es

String FileItem.getFieldName () Obtiene el valor del atributo de nombre del elemento del formulario

String FileItem.getString () Obtiene el valor del elemento de formulario actual

String FileItem.getName () Obtiene el nombre del archivo cargado

void FileItem.write (archivo) Escribe el archivo cargado en la ubicación del disco duro bombeado al que apunta el archivo de parámetros

1.3 Uso de la biblioteca de carga de archivos

public class UploadServlet extends HttpServlet {
    
    
    /**
     * 用来处理文件上传的数据
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //1.先判断上传的数据是否是多段的数据
        //ServletFileUpload.isMultipartContent(req)返回true就说明是多段的,返回false就说明不是
        if (ServletFileUpload.isMultipartContent(req)) {
    
    

            //创建 FileItemFactory 工厂实现类
            FileItemFactory fileItemFactory = new DiskFileItemFactory();
            //创建用于解析上传数据的工具类 ServletFileUpload
            ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
            //解析上传的数据,得到一个表单项 FileItem
            try {
    
    
                List<FileItem> list = servletFileUpload.parseRequest(req);

                //循环判断每一个表单项是普通类型还是上传的文件
                for (FileItem fileItem : list) {
    
    

                    if (fileItem.isFormField()) {
    
    
                        //普通表单项
                        System.out.println("表单项的name属性值:" + fileItem.getFieldName());
                        //参数 UTF-8 解决中文乱码问题
                        System.out.println("表单项的value属性值:" + fileItem.getString("UTF-8"));
                    } else {
    
    
                        //上传的文件
                        System.out.println("表单项的name属性值:" + fileItem.getFieldName());
                        System.out.println("上传的文件名:" + fileItem.getName());
                        fileItem.write(new File("d:\\study\\" + fileItem.getName()));
                    }

                }

            } catch (Exception e) {
    
    
                e.printStackTrace();
            }

        }
    }
}

"//": Este es un comentario de una sola línea, creo que todos lo saben, así que no diré más

"", "\": Por ejemplo, C: \ ls \ 123 \ JDBC2 \ bin, "" significa el separador de ruta predeterminado en el disco de la computadora, pero en el compilador de Java, se cambiará a "\" de manera predeterminada. formulario, especialmente cuando se requieren la ruta relativa y la ruta absoluta para la secuencia io, y se utilizará la carga posterior de archivos de configuración (carga), etc.

"/": Equivale a "\" en la ruta, que son dos formas de escribir la ruta Java

2. Descarga de archivos

Inserte la descripción de la imagen aquí

Descargue la descripción de la API de uso común:
response.getOutputStream ();
servletContext.getResourceAsStream ();
servletContext.getMimeType ();
response.setContentType ();

response.setHeader ("Content-Disposition", "adjunto; fileName = 1.jpg");
Este encabezado de respuesta le dice al navegador. Esto se va a descargar. Y adjunto significa adjunto, que es un archivo descargado. Después de fileName =, significa el nombre del archivo descargado.

Después de completar los dos pasos anteriores, la descarga del archivo no es un problema. Pero si el archivo que queremos descargar es un nombre chino. Verá que la descarga no puede mostrar correctamente el nombre chino correcto.

La razón es que el encabezado de respuesta no puede contener caracteres chinos, solo códigos ASCII.

public class DownLoad extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        //1.获取需要下载的文件名
        String downloadFileName = "a.jpg";

        //2.读取要下载的文件内容(通过 ServletContext 对象可以获取)
        ServletContext servletContext = getServletContext();
        //获取要下载的文件类型
        String mimeType = servletContext.getMimeType("/file/" + downloadFileName);
        System.out.println("下载的文件类型:" + mimeType);

        //4.在回传之前,通过响应头告诉客户端返回的数据类型
        resp.setContentType(mimeType);
        //5.还要告诉客户端收到的数据适用于下载使用(还是使用响应头)
        // Content-Disposition 响应头,表示收到的数据怎么处理
        // attachment 表示附件,下载使用;
        // filename= 表示指定下载的文件名
        resp.setHeader("Content-Disposition","attachment;filename=" + downloadFileName);

        /*
        在服务器端,第一个 / 被解析为: http://ip:port/工程名/  映射到代码的 web 目录
         */
        InputStream resourceAsStream = servletContext.getResourceAsStream("/file/" + downloadFileName);
        // 原先是用 byte[] 来读,这里改变一下,通过使用 commons-io-1.4.jar 包里已经写好的工具类 IOUtils
        // 获取响应的输出流
        OutputStream outputStream = resp.getOutputStream();

        //3.把下载的文件内容传回客户端
        // 读取输出流中全部的数据,复制给输出流,输出给客户端
        IOUtils.copy(resourceAsStream,outputStream);
    }
}

Además:

resp.setHeader("Content-Disposition","attachment;filename=中国.jpg");

El nombre del archivo descargado aparecerá distorsionado y esta sección debe estar codificada por codificador.

resp.setHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode("中国.jpg","UTF-8"));

Solución del problema confuso del nombre chino adjunto:

Solución 1: URLEncoder resuelve el problema del nombre chino de los archivos adjuntos en los navegadores IE y Google

Si el navegador del cliente es Internet Explorer o Google Chrome. Necesitamos usar la clase URLEncoder para codificar el nombre chino en UTF-8 primero.

Porque el navegador IE y el navegador de Google decodificarán y mostrarán el juego de caracteres UTF-8 después de recibir la cadena codificada.

// 把中文名进行 UTF-8 编码操作。 
String str = "attachment; fileName=" + URLEncoder.encode("中文.jpg", "UTF-8"); 
// 然后把编码后的字符串设置到响应头中 
response.setHeader("Content-Disposition", str);

Solución 2: codificación y decodificación BASE64 para resolver el problema del nombre chino de los archivos adjuntos en Firefox

Si el navegador del cliente es Firefox. Entonces necesitamos realizar la operación de codificación BASE64 en el nombre chino.

En este momento, debe codificar el encabezado de la solicitud Content-Disposition: adjunto; nombre de archivo = nombre chino en:
Content-Disposition: adjunto; nombre de archivo ==? Charset? B? Xxxxx? =

=? charset? B? xxxxx? = Ahora explicaremos este párrafo.
=?
juego de caracteres
B
xxxx
? =

Operaciones de codificación y decodificación BASE64:

Porque Firefox usa el método de codificación y decodificación BASE64 para restaurar los caracteres chinos en la respuesta. Por lo tanto, debe utilizar la clase BASE64Encoder para las operaciones de codificación.

// 使用下面的格式进行 BASE64 编码后
String str = "attachment; fileName=" + "=?utf-8?B?" 
    + new BASE64Encoder().encode("中文.jpg".getBytes("utf-8")) + "?=";
// 设置到响应头中
response.setHeader("Content-Disposition", str);

Entonces, ¿cómo resolvemos los dos métodos de codificación y decodificación diferentes anteriores? Solo necesitamos determinar qué navegador es juzgando la información del navegador transportada por el encabezado de solicitud de Usuario-Agente en el encabezado de solicitud.

como sigue:

String ua = request.getHeader("User-Agent");
// 判断是否是火狐浏览器
if (ua.contains("Firefox")) {
    
    
// 使用下面的格式进行 BASE64 编码后
String str = "attachment; fileName=" + "=?utf-8?B?"
    + new BASE64Encoder().encode("中文.jpg".getBytes("utf-8")) + "?=";
// 设置到响应头中
response.setHeader("Content-Disposition", str);
} else {
    
    
// 把中文名进行 UTF-8 编码操作。
String str = "attachment; fileName=" + URLEncoder.encode("中文.jpg", "UTF-8");
// 然后把编码后的字符串设置到响应头中
response.setHeader("Content-Disposition", str);
}

Supongo que te gusta

Origin blog.csdn.net/weixin_45024585/article/details/112748075
Recomendado
Clasificación