day09-攔截器&文件上傳

来源:https://www.cnblogs.com/liyuelian/archive/2023/03/21/17242028.html
-Advertisement-
Play Games

攔截器&文件上傳 1.攔截器-Interceptor 1.1攔截器概念 攔截器 攔截器(Interceptor):是一種動態攔截方法調用的機制,在SpringMVC中動態攔截控制器方法的執行。在SpringBoot中,攔截器是開發的常用手段,要用來登錄驗證、性能檢查、日誌記錄等 (1)SpringB ...


攔截器&文件上傳

1.攔截器-Interceptor

1.1攔截器概念

攔截器

攔截器(Interceptor):是一種動態攔截方法調用的機制,在SpringMVC中動態攔截控制器方法的執行。在SpringBoot中,攔截器是開發的常用手段,要用來登錄驗證、性能檢查、日誌記錄等

(1)SpringBoot中攔截器實現的基本步驟:

  1. 編寫一個攔截器實現 HandlerInterceptor 介面
  2. 攔截器註冊到配置類中(實現 WebMvcConfigurer 的 addInterceptors)
  3. 指定攔截規則
8359fb5520a740ae94f51033c22e97dc ff42d4439cac4f68a9eb495389a9a208

(2)攔截器執行順序

  • preHandle()
  • if return true
    • controller
    • postHandle()
    • afterCompletion()
  • else-if return false
    • 結束

1.2應用實例

演示:使用攔截器防止用戶非法登錄。以day08-2中的綜合案例為例子,之前是使用session校驗用戶有沒有登錄過,現在使用攔截器統一校驗:瀏覽器輸入地址請求某個頁面,如果此前用戶沒有登錄過,就返回登錄頁面,並提示信息。

(1)創建攔截器LoginInterceptor.java

package com.li.thymeleaf.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author 李
 * @version 1.0
 */
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    /**
     * preHandle在目標方法執行前被調用
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String requestURI = request.getRequestURI();
        log.info("preHandle()攔截到的URI=" + requestURI);
        //進行登錄校驗
        Object loginAdmin = request.getSession().getAttribute("loginAdmin");
        if (null != loginAdmin) {//成功登錄過
            //放行
            return true;
        }
        //沒有登錄過
        request.setAttribute("msg", "沒有登錄過,請登錄!");
        request.getRequestDispatcher("/login").forward(request, response);
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("postHandle()被執行...");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("afterCompletion()被執行...");
    }
}

(2)將攔截器註冊到配置類中(實現 WebMvcConfigurer 的 addInterceptors),並指定攔截規則(攔截路徑)

除了這種方式之外,也可以通過註冊轉換器的方式來註冊攔截器

package com.li.thymeleaf.config;

import com.li.thymeleaf.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author 李
 * @version 1.0
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //addInterceptor註冊自定義攔截器
        //addPathPatterns指定攔截器規則(攔截所有請求/**)
        //excludePathPatterns排除指定的路徑,不攔截
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/", "/login", "/images/**");
    }
}

其他代碼略,詳見上一篇day08-2中的綜合案例

image-20230321191317810 image-20230321185707616

1.3註意事項和細節

1.3.1URI和URL的區別

URI和URL的區別

1、URI是以一種抽象的,高層次概念定義統一資源標識;而URL則是具體的資源標識的方式,URL是一種URI。

2、格式不同:

  • URL的格式一般由下列三部分組成:第一部分是協議(或稱為服務方式);第二部分是存有該資源的主機IP地址(有時也包括埠號);第三部分是主機資源的具體地址。

  • URI一般由三部分組成:訪問資源的命名機制;存放資源的主機名;資源自身的名稱,由路徑表示。

3、在Java的URI中,一個URI實例可以代表絕對的,也可以是相對的,只要它符合URI的語法規則。而URL類則不僅符合語義,還包含了定位該資源的信息,因此它不能是相對的,schema(protocol)必須被指定。

例如:

URI=/manage.html
URL=http://localhost:8080/manage.html

URI,是uniform resource identifier,統一資源標識符,用來唯一的標識一個資源。而URL是uniform resource locator,統一資源定位器,它是一種具體的URI,即URL可以用來標識一個資源,而且還指明瞭如何locate這個資源。

因此,URL是一種具體的URI,它不僅唯一標識資源,而且還提供了定位該資源的信息。URI是一種語義上的抽象概念,可以是絕對的,也可以是相對的,而URL則必須提供足夠的信息來定位,所以,是絕對的,而通常說的relative URL,則是針對另一個absolute URL,本質上還是絕對的。

1.3.2攔截器的第二種註冊方式

package com.li.thymeleaf.config;

import com.li.thymeleaf.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author 李
 * @version 1.0
 * 註冊攔截器的第二種方式
 */
@Configuration
public class WebConfig2 {
    @Bean
    public WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                registry.addInterceptor(new LoginInterceptor())
                        .addPathPatterns("/**")
                        .excludePathPatterns("/", "/login", "/images/**");
            }
        };
    }
}

1.3.3攔截器和過濾器的區別

攔截器與過濾器的區別

2.文件上傳

在SpringBoot中,怎麼實現文件上傳?

我們先來回顧一下SpringMVC的文件上傳。SpringMVC的文件上傳通過配置 MultipartResolver 來實現。SpringMVC 上下文預設沒有裝配 MultipartResolver ,因此預設情況下不能處理文件的上傳工作。如果要使用 Spring 文件上傳功能,要先在上下文(容器文件)中配置 MultipartResolver 。

而SpringBoot使用的其實就是簡化後的SpringMVC上傳功能。

2.1應用實例

例子:演示SpringBoot中通過表單註冊用戶,並支持上傳圖片

(1)使用thymeleaf引擎,在templates目錄下中創建一個upload.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>upload</title>
</head>
<body bgcolor="#ced3fe">
<div style="text-align: center">
    <h1>註冊用戶</h1>
    <img src="images/login.jpg" width="35px"><br/><br/>
    <form action="#" th:action="@{/upload}" method="post">
        用戶名:<input type="text" style="width: 150px" name="name"/><br/><br/>
        郵 件: <input type="text" style="width: 150px" name="email"/><br/><br/>
        年 齡: <input type="text" style="width: 150px" name="age"/><br/><br/>
        職 位: <input type="text" style="width: 150px" name="job"/><br/><br/>
        頭 像: <input type="file" style="width: 150px" name="header"/><br/><br/>
        <!--multiple表示可以同時選擇多個文件-->
        寵 物: <input type="file" style="width: 150px" name="photos" multiple/><br/><br/>
        <input type="submit" value="註冊"/>
        <input type="reset" value="重新填寫"/>
    </form>
</div>
</body>
</html>

(2)創建控制器UploadController.java

package com.li.thymeleaf.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;

/**
 * @author 李
 * @version 1.0
 */
@Controller
@Slf4j
public class UploadController {
    //處理轉發到用戶註冊(瀏覽器不能直接訪問templates目錄下的資源)
    @GetMapping("/register")
    public String register() {
        //這裡沒有配置就可以進行視圖解析,是因為導入了thymeleaf-starter
        return "upload";//視圖解析,轉發到templates/upload.html
    }

    @PostMapping("/upload")
    @ResponseBody
    public String upload(@RequestParam("name") String name,
                         @RequestParam("email") String email,
                         @RequestParam("age") Integer age,
                         @RequestParam("job") String job,
                         @RequestParam("header") MultipartFile header,
                         @RequestParam("photos") MultipartFile[] photos)
            throws IOException {
        //輸出獲取到的信息
        log.info("上傳的信息 name={} email={} age={} job={} header={} photos={}",
                name, email, age, job, header.getSize(), photos.length);

        //如果信息都能成功獲取,就將文件保存到指定的磁碟路徑下(動態創建目錄)
        //比如放到D:\IDEA-workspace\thymeleaf\target\classes\static\images\upload\下

        //獲取類路徑
        String path = ResourceUtils.getURL("classpath:").getPath();
        log.info("path={}", path);//path=D:/IDEA-workspace/thymeleaf/target/classes/

        //構建文件要保存的路徑
        File file = new File(path + "static/images/upload/");
        log.info("AbsolutePath={}", file.getAbsolutePath());
        //AbsolutePath=D:\IDEA-workspace\thymeleaf\target\classes\static\images\upload

        //判斷該目錄是否存在
        if (!file.exists()) {//目錄不存在
            file.mkdirs();//創建目錄
        }

        if (!header.isEmpty()) {//處理頭像
            //獲取上傳的文件的名稱
            String originalFilename = header.getOriginalFilename();
            //指定保存文件的絕對路徑
            header.transferTo(new File(file.getAbsolutePath() + "/" +
                    originalFilename));
        }

        if (photos.length > 0) {//處理寵物圖片
            for (MultipartFile photo : photos) {//有多張photo,因此迴圈處理
                if (!photo.isEmpty()) {
                    //獲取上傳的文件的名稱
                    String originalFilename = photo.getOriginalFilename();
                    //指定保存文件的絕對路徑
                    photo.transferTo(new File(file.getAbsolutePath() + "/" +
                            originalFilename));
                }
            }
        }

        return "註冊用戶/文件上傳成功";
    }
}

(3)瀏覽器訪問表單,填寫數據並提交

image-20230321220326857

後臺輸出:

image-20230321222429908

對應的磁碟路徑成功保存上傳的圖片:

image-20230321222323172

2.2問題和解決方案

上述的案例還存在一些問題,如果上傳的圖片過大會拋出異常:

image-20230321223521051

可以在配置文件中修改文件上傳的參數:

spring:
  servlet:
    multipart:
      max-file-size: 10MB #指定文件大小最大值,預設1MB
      max-request-size: 50MB #指定每次請求的最大值,預設為10MB

2.3擴展

(1)解決文件覆蓋問題:如果文件名相同,會出現覆蓋現象,如何解決?

  • 解決方案:對上傳的文件名進行處理,比如增加一個首碼(UUID+System.currentTimeMillis)
image-20230321225419283 image-20230321225643567

(2)解決文件分目錄存放問題:如果將所有文件都放到一個目錄下,當上傳的文件增多時,會造成訪問文件速度變慢(而且某些操作系統一個目錄下存放文件的數量也有限制)

  • 解決方案:根據當前日期生成一個目錄,同一天上傳的目錄統一存放到一個文件夾下,年/月/日 目錄。
package com.li.thymeleaf.utils;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author 李
 * @version 1.0
 */
public class WebUtils {
    //定義一個文件上傳的路徑
    public static String UPLOAD_FILE_DIRECTORY = "static/images/upload/";

    //編寫方法,根據當前年月日生成一個目錄
    public static String getUploadFileDirectory() {
        return UPLOAD_FILE_DIRECTORY +
                new SimpleDateFormat("yyyy/MM/dd").format(new Date());
    }
}

然後在構建文件保存目錄的時候調用上面的方法:(修改UploadController.java)

image-20230321230620819

測試結果:

image-20230321230715262
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 今天在學習 React 文檔,列表渲染一節中提及到一個關於 key 綁定索引值(index)性能的問題: React 官方文檔原文:“如果列表項目的順序可能會變化,我們不建議使用索引來用作 key 值,因為這樣做會導致性能變差,還可能引起組件狀態的問題”。 查閱網上的博客,確實有對 Vue 列表渲染 ...
  • (1)使用vue-cli創建: ## 安裝或者升級 npm install -g @vue/cli ## 保證 vue cli 版本在 4.5.0 以上 vue --version ## 創建項目 vue create my-project 然後的步驟: Please pick a preset - ...
  • 本文將介紹一個角向漸變的一個非常有意思的小技巧! 我們嘗試使用 CSS 繪製如下圖形: 在之前,類似的圖案,其實我們有嘗試過,在 單標簽實現複雜的棋盤佈局 一文中,我們用單標簽實現了這樣一個棋盤佈局: 那麼,本文有什麼特殊之處呢?讓我們一探究竟。 快速實現網格佈局 首先,上述的佈局還是希望使用一個標 ...
  • 橋接模式(Bridge Pattern)是一種結構型設計模式,用於將一個大類或一系列緊密相關的類拆分為抽象和實現兩個獨立的層次結構,從而能夠更好地組合和擴展這些類。 在前端開發中,橋接模式通常用於處理 UI 組件的複雜性,將組件的抽象與實現分離,使得它們能夠獨立地變化。通過橋接模式,我們可以讓組件的 ...
  • 概述 KCP協議結合了TCP和UDP協議的特點,是一個快速可靠的協議。 引述官方介紹: KCP是一個快速可靠協議,能以比 TCP浪費10%-20%的帶寬的代價,換取平均延遲降低 30%-40%,且最大延遲降低三倍的傳輸效果。純演算法實現,並不負責底層協議(如UDP)的收發,需要使用者自己定義下層數據的 ...
  • TCP/IP協議中有兩個具有代表性的傳輸層協議,分別是TCP協議和UDP協議。TCP協議全稱傳輸控制協議,是一種面向連接的、可靠的、基於位元組流的傳輸層通信協議。UDP協議全稱用戶數據報協議,是 TCP/IP協議模型傳輸層的無連接協議,它既不建立連接,也不檢查目標電腦是否已準備好接收,該協議只是將數... ...
  • 複習 Vue 1.vue的使用步驟: (1)導入vue.js (2)創建除body以外最大的div標簽,給定id值 (3)創建vue對象 new Vue({ "el":"#app", "data":{}, //定義變數 "methods":{}, //定義方法 "beforeCreate":func ...
  • Mybatis 環境: JDK1.8 Mysql maven IDEA 回顧: JDBC Mysql Java基礎 Maven Junit SSM框架:配置文件的。最好的方式:看官網文檔; 1、簡介 1.1、什麼是Mybatis MyBatis 是一款優秀的持久層框架 它支持自定義 SQL、存儲過程 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...