從零開始搭建日誌系統(4)-發表文章圖片上傳

git項目地址
git項目地址

https://github.com/MrITzhongzi/blog-system.git

邏輯梳理
  1. 每個用戶可以發表多個文章
  2. 每個文章都有自己的圖片
  3. 文章有自己的點贊,瀏覽量,評論數
遇到的坑
  1. 繼承WebMvcConfigurationSupport導致springboot靜態資源無法訪問
    因爲要設置api的攔截器,所以需要繼承這個類WebMvcConfigurationSupport並重寫裏面的addInterceptors方法,如下所示:
@Configuration
public class IntercepterConfig extends WebMvcConfigurationSupport {
    @Resource
    private LoginIntercepter loginIntercepter;

    /**
     * 配置api接口過濾器
     * @param registry
     */
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        List<String> excludePath = new ArrayList<>();
        excludePath.add("/api/user/register");
        excludePath.add("/api/user/login");
        //一下是爲了測試臨時改動
        excludePath.add("/api/img/**");

        List<String> includePath = new ArrayList<>();
        includePath.add("/api/**");

        registry.addInterceptor(loginIntercepter)
                .addPathPatterns(includePath)
                .excludePathPatterns(excludePath);

        super.addInterceptors(registry);
    }
}

繼承這個類會導致springboot內部設置的靜態資源訪問路徑被重製,所以需要重新設置一下,不然靜態資源無法訪問,如下所示:

package com.lhw.blog.config;

import com.lhw.blog.intercepter.LoginIntercepter;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * @description:
 * @author: lihongwei
 * @time: 2020/4/10 3:04 下午
 */
@Configuration
public class IntercepterConfig extends WebMvcConfigurationSupport {
    @Resource
    private LoginIntercepter loginIntercepter;

    /**
     * 配置api接口過濾器
     * @param registry
     */
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        List<String> excludePath = new ArrayList<>();
        excludePath.add("/api/user/register");
        excludePath.add("/api/user/login");
        //一下是爲了測試臨時改動
        excludePath.add("/api/img/**");

        List<String> includePath = new ArrayList<>();
        includePath.add("/api/**");

        registry.addInterceptor(loginIntercepter)
                .addPathPatterns(includePath)
                .excludePathPatterns(excludePath);

        super.addInterceptors(registry);
    }

    /**
     * 重新配置可以訪問靜態資源路徑,不配置這個 會導致靜態資源無法訪問
     * @param registry
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        //重新配置靜態資源
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/resources/")
                .addResourceLocations("classpath:/static/")
                .addResourceLocations("classpath:/public/");
        super.addResourceHandlers(registry);
    }
}

  1. springboot @Value注入失敗問題
  • 不能作用於靜態變量(static);
  • 不能作用於常量(final);
  • 不能在非註冊的類中使用(類需要被註冊在spring上下文中,如用@Service,@RestController,@Component等);
  • 使用這個類時,只能通過依賴注入的方式,用new的方式是不會自動注入這些配置的。

參考文章
https://blog.csdn.net/ITzhongzi/article/details/105489035

只要用到@Value的類,只能用註解的方式生成實例。不能用new

pom文件配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.lhw</groupId>
    <artifactId>blog</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>blog</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!-- 引入mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>

        <!-- MySQL的JDBC驅動包  -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version> 8.0.19</version>
        </dependency>

        <!-- JWT工具類 -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>

        <!-- json工具 -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.0</version>
        </dependency>
        <!--七牛雲-->
        <dependency>
            <groupId>com.qiniu</groupId>
            <artifactId>qiniu-java-sdk</artifactId>
            <version>[7.2.0, 7.2.99]</version>
        </dependency>

        <!-- io 工具包-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>7.1.0</version>
            <scope>compile</scope>
        </dependency>


    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
用到的工具類
  • MultipartFile和 File類轉換
package com.lhw.blog.tool;

import org.apache.commons.io.IOUtils;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;

public class FileUtils {

    public static MultipartFile file2MultipartFile(File file){
        try {
            FileInputStream fileInputStream = new FileInputStream(file);
            MultipartFile multipartFile = new MockMultipartFile("file", file.getName(), "text/plain",IOUtils.toByteArray(fileInputStream));
//            InputStream inputStream = multipartFile.getInputStream();
            return multipartFile;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static File multipartFile2File(MultipartFile multipartFile){
        File file = null;
        InputStream inputStream = null;
        try {
            inputStream = multipartFile.getInputStream();
            file = new File(multipartFile.getOriginalFilename());
            inputStreamToFile(inputStream, file);
            return file;
        } catch (Exception e) {}

        return null;
    }

    public static void inputStreamToFile(InputStream ins, File file) {
        try {
            OutputStream os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            ins.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

  • 七牛圖片服務器工具
package com.lhw.blog.tool;

import com.google.gson.Gson;
import com.lhw.blog.config.QiniuConfig;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.*;
import java.net.URLEncoder;

/**
 * @description:
 * @author: lihongwei
 * @time: 2020/4/10 4:05 下午
 */
@Component
public class QiniuUtils {

    @Autowired
    private QiniuConfig qiniuConfig;

    /**
     *
     * 上傳圖片到七牛雲
     * @param file
     * @return
     */
    public DefaultPutRet uploadFile(File file) {
        //構造一個帶指定 Region 對象的配置類
        Configuration cfg = new Configuration(Region.autoRegion());
        //...其他參數參考類註釋
        UploadManager uploadManager = new UploadManager(cfg);
        //...生成上傳憑證,然後準備上傳
        String accessKey = qiniuConfig.accessKey;
        String secretKey = qiniuConfig.secretKey;
        String bucket = qiniuConfig.bucket;
        String suffix = file.getName().substring(file.getName().lastIndexOf("."));
        //默認不指定key的情況下,以文件內容的hash值作爲文件名 (這裏的key就是存在七牛雲上的文件名)
        String key = UuidUtils.getUUID() + suffix;
        try {
            Auth auth = Auth.create(accessKey, secretKey);
            String upToken = auth.uploadToken(bucket);
            InputStream is = new FileInputStream(file);
            try {
                Response response = uploadManager.put(is, key, upToken, null, null);
                //解析上傳成功的結果
                DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
                System.out.println(putRet.key);
                System.out.println(putRet.hash);

                return putRet;
            } catch (QiniuException ex) {
                Response r = ex.response;
                System.err.println(r.toString());
                try {
                    System.err.println(r.bodyString());
                } catch (QiniuException ex2) {
                    //ignore
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 獲取七牛雲私有空間的圖片路徑
     * @param filename
     * @return
     */
    public String getPrivateImagePath(String filename){
        //"1.png"
        String fileName = filename;
        String domainOfBucket = "http://q8eezq1qm.bkt.clouddn.com";
        String encodedFileName = null;
        try {
            encodedFileName = URLEncoder.encode(fileName, "utf-8").replace("+", "%20");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        String publicUrl = String.format("%s/%s", domainOfBucket, encodedFileName);
        String accessKey = qiniuConfig.accessKey;
        String secretKey = qiniuConfig.secretKey;
        Auth auth = Auth.create(accessKey, secretKey);
        //1小時,可以自定義鏈接過期時間
        long expireInSeconds = 3600;
        String finalUrl = auth.privateDownloadUrl(publicUrl, expireInSeconds);
        System.out.println(finalUrl);
        return "abc";
    }

    /**
     * 獲取七牛雲共有空間的圖片路徑
     * @param filename
     * @return
     */
    public String getPublicImagePath(String filename){
        String fileName = filename;
        String domainOfBucket = "http://q8pvrckr7.bkt.clouddn.com";
        String finalUrl = String.format("%s/%s", domainOfBucket, fileName);
        System.out.println(finalUrl);
        return "";
    }
}

  • 獲取uuid工具
package com.lhw.blog.tool;

import java.util.UUID;

public class UuidUtils {

    public static String getUUID(){
        return UUID.randomUUID().toString().replaceAll("-", "");
    }
}

增加了過濾器 (驗證token)
  • 定義過濾器
package com.lhw.blog.intercepter;

import com.google.gson.Gson;
import com.lhw.blog.tool.JWTUtils;
import com.lhw.blog.tool.JsonBuilder;
import io.jsonwebtoken.Claims;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @description: 進入controller之前進行攔截
 * @author: lihongwei
 * @time: 2020/4/10 2:45 下午
 */
@Component
public class LoginIntercepter implements HandlerInterceptor {

    private static final Gson gson = new Gson();

    /**
     * 進入controller之前進行攔截,檢查token是否合法
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");
        if(token == null) {
            token = request.getParameter("token");
        }
        if(token != null) {
            Claims claims = JWTUtils.checkJWT(token);
            if(claims != null) {
                Integer userId = (Integer) claims.get("id");
                request.setAttribute("user_id", userId);
                return true;

            }
        }
        sendJsonMessage(response, JsonBuilder.buildError("請登錄"));
        return false;
    }

    /**
     * 響應給前臺數據
     * @param response
     * @param obj
     */
    public static void sendJsonMessage(HttpServletResponse response, Object obj){
        response.setContentType("application/json; charset=utf-8");
        PrintWriter writer = null;
        try {
            writer = response.getWriter();
            writer.println(gson.toJson(obj));
            writer.close();
            response.flushBuffer();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

  • 使用過濾器
package com.lhw.blog.config;

import com.lhw.blog.intercepter.LoginIntercepter;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * @description:
 * @author: lihongwei
 * @time: 2020/4/10 3:04 下午
 */
@Configuration
public class IntercepterConfig extends WebMvcConfigurationSupport {
    @Resource
    private LoginIntercepter loginIntercepter;

    /**
     * 配置api接口過濾器
     * @param registry
     */
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        List<String> excludePath = new ArrayList<>();
        excludePath.add("/api/user/register");
        excludePath.add("/api/user/login");
        //一下是爲了測試臨時改動
        excludePath.add("/api/img/**");

        List<String> includePath = new ArrayList<>();
        includePath.add("/api/**");

        registry.addInterceptor(loginIntercepter)
                .addPathPatterns(includePath)
                .excludePathPatterns(excludePath);

        super.addInterceptors(registry);
    }

    /**
     * 重新配置可以訪問靜態資源路徑,不配置這個 會導致靜態資源無法訪問
     * @param registry
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        //重新配置靜態資源
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/resources/")
                .addResourceLocations("classpath:/static/")
                .addResourceLocations("classpath:/public/");
        super.addResourceHandlers(registry);
    }
}

增加了跨域配置
package com.lhw.blog.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

/**
 * @description:
 * @author: lihongwei
 * @time: 2020/4/10 4:54 下午
 */
@Configuration
public class Cors extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")  //所有路徑 允許跨域
                .allowedOrigins("*") // 允許某個域名下
                .allowedMethods("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH") // 允許跨域的方法
                .allowCredentials(true) //
                .maxAge(3600); //有效期
    }
}
controller層新增api
  • 文章controller
package com.lhw.blog.controller;

import com.lhw.blog.domain.LhwArticles;
import com.lhw.blog.service.ArticleService;
import com.lhw.blog.tool.JsonBuilder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * @description:
 * @author: lihongwei
 * @time: 2020/4/10 11:13 上午
 */
@RestController
@RequestMapping("/api/article")
public class ArticleController {

    @Resource
    private ArticleService articleService;

    /**
     * 發表文章 api
     * @return
     */
    @RequestMapping(path = "/publish", method = RequestMethod.POST)
    public JsonBuilder publishAticle(@RequestParam(value = "title") String title,
                                     @RequestParam(value = "content") String content,
                                     HttpServletRequest request){
        //查詢用戶的相關信息
        Integer userId = (Integer)request.getAttribute("user_id");
        LhwArticles article = new LhwArticles();
        article.setUserId(userId);
        article.setArticleTitle(title);
        article.setArticleContent(content);
        try {
            int row = articleService.insertArticle(article);
            if(row == 0) {
                return JsonBuilder.buildError("文章插入失敗");
            }
            return JsonBuilder.buildSuccess("文章插入成功");

        } catch (Exception e){}


        return JsonBuilder.buildError("文章插入失敗");
    }
}

  • 圖片controller
package com.lhw.blog.controller;

import com.lhw.blog.tool.FileUtils;
import com.lhw.blog.tool.JsonBuilder;
import com.lhw.blog.tool.QiniuUtils;
import com.qiniu.storage.model.DefaultPutRet;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;

/**
 * @description: 圖片上傳 api
 * @author: lihongwei
 * @time: 2020/4/10 4:05 下午
 */
@RestController
@RequestMapping("/api/img")
public class ImageController {

    @Autowired
    private QiniuUtils qiniuUtils;

    @RequestMapping(path = "/upload_img", method = RequestMethod.POST)
    public JsonBuilder uploadFile(@RequestParam("img") MultipartFile file) {

        File requestFile = FileUtils.multipartFile2File(file);
        DefaultPutRet defaultPutRet = qiniuUtils.uploadFile(requestFile);
        if(defaultPutRet != null) {
            return JsonBuilder.buildSuccess("上傳成功", defaultPutRet);
        }

        return JsonBuilder.buildError("上傳失敗");
    }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章