Spring Boot - AJAX 跨域图片上传

Spring Boot - AJAX 跨域图片上传

Spring Boot 2.2.4.RELEASE

项目结构

后端

在这里插入图片描述

新建项目,引入依赖:

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

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

<!-- hutool -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.1.2</version>
</dependency>

<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

logback-spring.xml 日志配置:

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <!-- 定义日志文件的输出路径 -->
    <property name="USER_HOME" value="G:/log" />
    
    <!-- 输出到控制台 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %-5level [%-24thread] %logger - %msg%n</pattern>
        </encoder>
    </appender>
    
    <!-- 基于大小以及时间的轮转策略 -->
    <!-- 参考:http://www.logback.cn/04%E7%AC%AC%E5%9B%9B%E7%AB%A0Appenders.html -->
    <appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 要写入文件的名称。如果文件不存在,则新建。 -->
        <file>${USER_HOME}/logback.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${USER_HOME}/%d{yyyyMMdd}/logback-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>20KB</maxFileSize>
            <!-- 最多保留多少数量的归档文件,将会异步删除旧的文件。 -->
            <maxHistory>30</maxHistory>
            <!-- 所有归档文件总的大小。当达到这个大小后,旧的归档文件将会被异步的删除。 -->
            <totalSizeCap>1000MB</totalSizeCap>
        </rollingPolicy>

        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %-5level [%-24thread] %logger - %msg%n</pattern>
        </encoder>
    </appender>
    
    <logger name="com.mk.controller" level="DEBUG" ></logger>
    
    <!-- 日志输出级别 -->
    <root level="info">
        <appender-ref ref="ROLLING" />
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

工具类,用于获取文件扩展名:

public class CommonUtils {
    
    /**
     * <p> 获取文件的类型名称,即扩展名。
     * <p> 示例:
     * <p> 执行 <code>CommonUtils.getTypeName("file.txt", true);</code>,返回 <code>".txt"</code>
     * <p> 执行 <code>CommonUtils.getTypeName("file.txt", false);</code>,返回 <code>"txt"</code>
     * @param filename 文件名称
     * @param dot 是否保留类型名称前面的点(.),<code>true</code> 保留,否则去除。
     * @return 如果存在,返回文件类型名称。否则返回 <code>null</code>
     */
    public static String getTypeName(String filename, boolean dot) {
        if ((filename == null) || (filename.equals(""))) {
            return null;
        }
        
        int lastOccurrence = filename.lastIndexOf(".");
        if (lastOccurrence != -1) {
            if (!dot) {
                lastOccurrence += 1;
            }
            String typeName = filename.substring(lastOccurrence);
            return typeName;
        }
        
        return null; // 该文件不存在类型名称
    }
}

附件控制器,用于处理图片的上传和查看:

import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

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

import org.apache.commons.io.FileUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import com.mk.common.CommonUtils;

import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
@RequestMapping("/attachment")
public class AttachmentController {
    
    /**
     * 存在文件的目录
     */
    private String PATH = "G:" + File.separator + "20191108" + File.separator + "1";
    
    /**
     * 图片上传
     * 注意,此处没有对上传的文件进行任何校验,请谨慎使用。
     * @param file
     * @return
     */
    @CrossOrigin(origins = {"*"})
    @PostMapping("image/upload")
    public Map<String, Object> upload(@RequestParam(value = "file", required = false) MultipartFile file,
            HttpServletRequest request) {
//        ServletRequestAttributes servletRequestAttributes = 
//                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//        HttpServletRequest request = servletRequestAttributes.getRequest();
        String scheme = request.getScheme();
        int serverPort = request.getServerPort();
        String serverName = request.getServerName();
        
        String path = this.PATH;
        
        Map<String, Object> data = new HashMap<String, Object>();

        data.put("message", "图片上传失败!");
        data.put("url", "");
        if ((file == null) || file.isEmpty()) {
            return data;
        }
        
        String originalFilename = file.getOriginalFilename(); // 原文件名称
        
        // 因为上传的文件可能出现同名,所以需要重新分配一个新文件名称,防止出现同名文件被覆盖的情况。
        String dateFormatStr = DateUtil.format(new Date(), "yyyyMMddHHmmssSSS");
        String newFilename = dateFormatStr
                + CommonUtils.getTypeName(originalFilename, true); // 文件类型(扩展名)
        
        // 存放文件的临时目录
        File directory = new File(path);
        if (!directory.exists()) {
            directory.mkdirs(); // 创建目录
        }
        // 保存文件到指定位置
        File destination = new File(directory, newFilename);
        try {
            // Transfer the received file to the given destination file. 
//                file.transferTo(destination);
            FileUtils.writeByteArrayToFile(destination, file.getBytes()); // 效果同上
        } catch (IllegalStateException | IOException e) {
            log.error("保存用户上传的文件异常:", e);
            return data;
        }
        
        // 检查文件是否保存成功
        if (destination.exists()) {
            String url = scheme + "://" + serverName + ":" + serverPort + "/attachment/image/"  + newFilename;
            if (log.isDebugEnabled()) {
                log.debug("url: " + url);
            }
            data.put("message", "图片上传成功!");
            data.put("url", url);
        }
        
        if (log.isDebugEnabled()) {
            log.debug(data.toString());
        }
        
        return data;
    }

    /**
     * 根据传入的文件名称将对应文件写到响应输出流
     * @param response
     * @param filename
     */
    @CrossOrigin(origins = {"*"})
    @GetMapping("image/{filename}")
    public void image(HttpServletResponse response, @PathVariable String filename) {
        String path = this.PATH;
        
        if (log.isDebugEnabled()) {
            log.debug(filename);
        }
        
        // 参考:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Caching_FAQ
        // Set standard HTTP/1.1 no-cache headers
        response.setHeader("Cache-Control", "no-store, no-cache");
        response.setContentType("image/jpeg"); // return a jpeg
        
        // 将文件写到输出流
        ServletOutputStream out = null;
        try {
            out = response.getOutputStream();
            
            File file = new File(path , filename); // 存放图片的临时目录
            
            if (file.isFile()) {
                out.write(FileUtils.readFileToByteArray(file));
            }
            out.flush();
        } catch (IOException e) {
            log.error("查看图片文件异常:", e);
        } finally {
            IOUtils.closeQuietly(out);
        }
    }
}

前端

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>AJAX 跨域上传文件</title>
    </head>
    <body>
        <form id="form" enctype="multipart/form-data">
            <input id="file" type="file" name="file" />
            <input type="text" value="" placeholder="用于对比,请忽略我" />
            <br />
            <button type="button" id="upload">上传文件</button>
        </form>

        <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script>
        <script type="text/javascript">
            $(function() {
                $("#upload").click(function() {
                    var form = $("#form")[0];
                    // var form = document.getElementById("form"); // 同上,二选一
                    console.log("form: ", form);

                    // 参考:FormData - https://developer.mozilla.org/zh-CN/docs/Web/API/FormData
                    // 参考:FormData用法详解 - https://blog.csdn.net/zqian1994/article/details/79635413
                    var formData = new FormData(form);

                    // 调试
                    // console.log(formData);
                    // var keys = formData.keys(); // 表单中所有键,即带有 name 属性的输入标签
                    // while (true) {
                    //     var key = keys.next();
                    //     if (key.done == true) {
                    //         break;
                    //     }
                    //     console.log("key name: " + key.value);
                    // }

                    $.ajax({
                        type: 'post',
                        url: "http://localhost:8080/attachment/image/upload", // 图片上传接口
                        data: formData,
                        cache: false,
                        processData: false,
                        contentType: false,
                        success: function(data, textStatus, jqXHR) {
                            console.log(data);
                            console.log(textStatus);
                            console.log(jqXHR);
                        },
                        error: function(XMLHttpRequest, textStatus, errorThrown) {
                            console.log(XMLHttpRequest);
                            console.log(textStatus);
                            console.log(errorThrown);
                        }
                    });
                });
            });
        </script>
    </body>
</html>

注意:后端和前端是两个不同的项目。

测试

图片上传成功之后返回的数据:

在这里插入图片描述

点击链接查看图片:

在这里插入图片描述

参考

ajax 实现文件上传

FormData

FormData 用法详解

发布了55 篇原创文章 · 获赞 0 · 访问量 3180

猜你喜欢

转载自blog.csdn.net/qq_29761395/article/details/104181617
今日推荐