Upload e download de arquivos (superdetalhado)

O upload e download de arquivos são funções muito comuns. Em muitos sistemas ou software, o upload e download de arquivos são usados ​​com frequência. Por exemplo: QQ avatar, upload é usado. Existem também funções de upload e download de anexos na caixa de correio. O upload dos materiais anexados é aprovado no sistema OA.

1. Introdução ao upload de arquivo (ênfase)

  1. Deve haver uma tag de formulário, method = post request
  2. O EncType valor de atributo da marca de forma deve ser um formulário de dados / concatenada valor
  3. Use input type = file na tag do formulário para adicionar o arquivo carregado
  4. Grave o código do servidor (o programa servlet é usado aqui) para receber e processar os dados carregados.

encType = multipart / form-data significa que os dados enviados são emendados na forma de vários segmentos (um segmento de dados para cada item do formulário) e, em seguida, enviados ao servidor na forma de um fluxo binário.

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>

Em seguida, escreva um UploadServlet.java (reescreva o método doPost) e configure o endereço de acesso para uploadServlet (todos omitidos aqui).

1.1 Upload de arquivo, descrição do protocolo HTTP

Insira a descrição da imagem aqui

Content-Type indica o tipo de dados enviados

multipart / form-data representa os dados enviados, que são emendados na forma de vários segmentos (um segmento de dados para cada item de formulário) e, em seguida, enviados ao servidor na forma de um fluxo binário .

limite representa o separador de cada pedaço de dados

---- WebKitFormBoundaryfR6ijBPPqbdkIBkO é gerado aleatoriamente todas as vezes pelo navegador. É o separador de cada dado.

A seguir está um exemplo de que o servidor não está recebendo na forma de um fluxo:

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

Depois que o navegador carregou o arquivo, dois nulos foram gerados no console do idea.

A seguir está um exemplo de recebimento de dados na forma de um fluxo:

    @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 个
    }

Resultado:

Insira a descrição da imagem aqui

O formato do conteúdo de saída é semelhante ao corpo da solicitação, o código truncado no meio é a informação recebida, que é omitida no corpo da solicitação (torna-se uma linha em branco)

1.2 introdução da API comum commons-fileupload.jar

commons-fileupload.jar precisa contar com o pacote commons-io.jar, então temos que importar os dois pacotes.

A primeira etapa é importar dois pacotes jar:
commons-fileupload-1.2.1.jar
commons-io-1.4.jar

Nos pacotes commons-fileupload.jar e commons-io.jar, quais são as classes comumente usadas?
A classe ServletFileUpload é usada para analisar os dados carregados.
A classe FileItem representa cada item do formulário.

boolean ServletFileUpload.isMultipartContent (solicitação HttpServletRequest);
Determine se o formato de dados carregado atualmente é um formato multipartes.

publicList <FileItem> parseRequest (HttpServletRequestrequest)
analisa os dados carregados

boolean FileItem.isFormField ()
determina se o item de formulário atual é um item de formulário normal. O tipo de arquivo carregado é
verdadeiro, o que significa que o tipo normal de item de formulário é
falso, o que significa que o tipo de arquivo carregado é

String FileItem.getFieldName () Obtenha o valor do atributo de nome do item de formulário

String FileItem.getString () Obtém o valor do item de formulário atual

String FileItem.getName () Obtenha o nome do arquivo carregado

void FileItem.write (file) Grave o arquivo carregado no local do disco rígido bombeado apontado pelo arquivo de parâmetro

1.3 Uso da biblioteca fileupload

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 é um comentário de uma linha, acredito que todos sabem, então não direi mais

"", "\": Por exemplo, C: \ ls \ 123 \ JDBC2 \ bin, "" significa o separador de caminho padrão no disco do computador, mas no compilador Java, será alterado para "\" por padrão. formulário, especialmente quando o caminho relativo e o caminho absoluto são necessários para o fluxo io e o carregamento subsequente de arquivos de configuração (carregamento), etc. será usado.

"/": É equivalente a "\" no caminho, que são duas maneiras de escrever o caminho Java

2. Download de arquivo

Insira a descrição da imagem aqui

Baixe a descrição da API comumente usada:
response.getOutputStream ();
servletContext.getResourceAsStream ();
servletContext.getMimeType ();
response.setContentType ();

response.setHeader ("Content-Disposition", "attachment; fileName = 1.jpg");
Este cabeçalho de resposta informa ao navegador. Isso deve ser baixado. E anexo significa anexo, que é um arquivo baixado. Após fileName =, significa o nome do arquivo baixado.

Depois de concluir as duas etapas acima, o download do arquivo não é problema. Mas se o arquivo que queremos baixar for um nome chinês. Você descobrirá que o download não pode exibir o nome chinês correto corretamente.

O motivo é que o cabeçalho da resposta não pode conter caracteres chineses, apenas 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);
    }
}

Além disso:

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

O nome do arquivo baixado aparecerá distorcido e esta seção precisa ser codificada pelo codificador

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

Nome chinês em anexo solução de problema deturpado:

Solução 1: URLEncoder resolve o problema de nomes em chinês de anexos nos navegadores IE e Google

Se o navegador do cliente for Internet Explorer ou Google Chrome. Precisamos usar a classe URLEncoder para codificar o nome chinês em UTF-8 primeiro.

Porque os navegadores IE e Google irão decodificar e exibir no conjunto de caracteres UTF-8 após receber a string codificada.

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

Solução 2: codificação e decodificação BASE64 para resolver o problema de nomes chineses de anexos no Firefox

Se o navegador do cliente for Firefox. Em seguida, precisamos realizar a operação de codificação BASE64 no nome chinês.

Neste momento, você precisa codificar o cabeçalho da solicitação Content-Disposition: attachment; filename = nome chinês em:
Content-Disposition: attachment; filename ==? Charset? B? Xxxxx? =

=? charset? B? xxxxx? = Agora vamos explicar este parágrafo.
=? conjunto de
caracteres
B
xxxx
? =

Operações de codificação e decodificação BASE64:

Porque o Firefox usa o método de codificação e decodificação BASE64 para restaurar os caracteres chineses na resposta. Portanto, você precisa usar a classe BASE64Encoder para operações de codificação.

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

Então, como resolvemos os dois métodos diferentes de codificação e decodificação acima? Precisamos apenas determinar qual navegador é, julgando as informações do navegador transportadas pelo cabeçalho da solicitação User-Agent no cabeçalho da solicitação.

do seguinte modo:

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);
}

Acho que você gosta

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