41、Springboot 文件上传 采用Base64方式

时间:2025-03-19 07:40:21

引入依赖

<dependency>
    <groupId>commons-net</groupId>
    <artifactId>commons-net</artifactId>
    <version>3.6</version>
</dependency>
<dependency>
    <groupId></groupId>
    <artifactId>thumbnailator</artifactId>
    <version>0.4.8</version>
</dependency>
<dependency>
    <groupId>-lib</groupId>
    <artifactId>json-lib</artifactId>
    <version>2.4</version>
    <classifier>jdk15</classifier>
</dependency>

创建 Base64 工具类

package ;

import .Base64;
import .BASE64Decoder;
import .BASE64Encoder;

import .*;

public class BaseUtils {

    static BASE64Encoder encoder = new .BASE64Encoder();
    static BASE64Decoder decoder = new .BASE64Decoder();

    /**
     * 得到Base64 字符串
     */
    public static String getBase64String(File file) {
        FileInputStream fin = null;
        BufferedInputStream bin = null;
        ByteArrayOutputStream baos = null;
        BufferedOutputStream bout = null;
        try {
            // 建立读取文件的文件输出流
            fin = new FileInputStream(file);
            // 在文件输出流上安装节点流(更大效率读取)
            bin = new BufferedInputStream(fin);
            // 创建一个新的 byte 数组输出流,它具有指定大小的缓冲区容量
            baos = new ByteArrayOutputStream();
            // 创建一个新的缓冲输出流,以将数据写入指定的底层输出流
            bout = new BufferedOutputStream(baos);
            byte[] buffer = new byte[1024];
            int len = (buffer);
            while (len != -1) {
                (buffer, 0, len);
                len = (buffer);
            }
            // 刷新此输出流并强制写出所有缓冲的输出字节,必须这行代码,否则有可能有问题
            ();
            byte[] bytes = ();
            return Base64.encodeBase64String(bytes).trim();
        } catch (FileNotFoundException e) {
            ();
        } catch (IOException e) {
            ();
        } finally {
            try {
                ();
                ();
                // 关闭 ByteArrayOutputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException
                // IOException
                // ();
                ();
            } catch (IOException e) {
                ();
            }
        }
        return null;
    }
    /**
     * 将base64编码转换成文件
     */
    public static void base64StringToFile(String base64sString, String fileFullName) {
        BufferedInputStream bin = null;
        FileOutputStream fout = null;
        BufferedOutputStream bout = null;
        try {
            // 将base64编码的字符串解码成字节数组
            byte[] bytes = (base64sString);
            // 创建一个将bytes作为其缓冲区的ByteArrayInputStream对象
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            // 创建从底层输入流中读取数据的缓冲输入流对象
            bin = new BufferedInputStream(bais);
            // 指定输出的文件
            File file = new File(fileFullName);
            // 创建到指定文件的输出流
            fout = new FileOutputStream(file);
            // 为文件输出流对接缓冲输出流对象
            bout = new BufferedOutputStream(fout);
            byte[] buffers = new byte[1024];
            int len = (buffers);
            while (len != -1) {
                (buffers, 0, len);
                len = (buffers);
            }
            // 刷新此输出流并强制写出所有缓冲的输出字节,必须这行代码,否则有可能有问题
            ();
        } catch (IOException e) {
            ();
        } finally {
            try {
                ();
                ();
                ();
            } catch (IOException e) {
                ();
            }
        }
    }

    public static InputStream base64StrToInputStream(String base64string) {
        ByteArrayInputStream stream = null;
        try {
            BASE64Decoder decoder = new BASE64Decoder();
            byte[] bytes1 = (base64string);
            stream = new ByteArrayInputStream(bytes1);
        } catch (Exception e) {
            ();
        }
        return stream;
    }
}

解除上传文件大小限制,在文件中做下简单配置

spring: 
  http: 
    multipart: 
      max-file-size: 512MB
      max-request-size: 4096MB
upload:
  image:
    url: D:/fileUpload/images/
  file:
    url: D:/fileUpload/files/

由于开发的是测试案例,所以大小给的随意了一些,具体请参照自身业务需要设定,另外,如果实际运行过程中,还会存在大小限制而无法上传的问题,请看下Tomcat或者Nginx配置,这也是我遇到过的情况,

Tomcat下文件上传大小,默认为2M(2097152),需要对做下修改

<Connector port="8080"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" redirectPort="8443" acceptCount="100"
               debug="0" connectionTimeout="20000" 
               disableUploadTimeout="true" URIEncoding="GBK"
               maxPostSize="209715200"/>

Nginx下文件上传大小,需要对做下修改

http{
    ……
    client_max_body_size 200M;
    ……
}

设置完大小限制之后,都要重新启动下,使其生效

创建上传ImageController类

package ;

import ;
import ;
import ;
import ;
import ;
import .*;
import ;
import ;
import org.;
import org.;
import ;
import ;
import ;
import .*;
import ;

import ;
import ;
import ;
import .*;
import ;

@Controller
@RequestMapping("/image")
@CrossOrigin(origins = "*", maxAge = 3600)
public class ImageController {
    // 图片上传存储的路径
    @Value("${}")
    private String imageDir;

    @Value("${}")
    private String fileDir;

    private Logger logger = ();

    /**
     * FTP 相关配置
     */
    @Value("${}")
    private String userName;

    @Value("${}")
    private String passWord;

    @Value("${}")
    private String ip;

    @Value("${}")
    private int port;

    @Value("${}")
    private String CURRENT_DIR;

    @PostMapping(value = "/uploadImageForBase64")
    @ResponseBody
    public JsonResult uploadImageForBase64(
            @RequestParam(value = "type", required = true) String type,
            @RequestParam(value = "fileBase", required = true) String fileBase,
            @RequestParam(value = "delPath", required = false, defaultValue = "") String delPath,
            HttpServletRequest request,
            HttpServletResponse response) throws FileNotFoundException {
        if((fileBase)){
            return (Const.ERROR_CODE, "请先选择一张图片上传", null);
        }
        try {
            // 执行删除程序
            DelService delTask = new DelService(ip, port, userName, passWord, delPath);
            Thread d = new Thread(delTask);
            ();
        }catch (Exception e){

        }
        String ext = ".png";
        String fileName = ();
        // 创建目录
        String relativePath = ().concat((new Date(), "yyyy/MM/dd"));
        File dir = new File(()+ relativePath);
        if(!()){
            ();
        }
        
        String newFileBase = "";
        if((",")>1) {
            newFileBase = ((",") + 1);
        }else{
            newFileBase = fileBase;
        }
        // 生产原图
        String normal = ().concat().concat(fileName).concat(ext);
        BaseUtils.base64StringToFile(newFileBase, normal);
        File file = new File(normal);
        // 生产中图
        String middle = ().concat().concat(fileName).concat(Const.IMG_MIDDLE).concat(ext);
        // 生产缩略图
        String thumb = ().concat().concat(fileName).concat(Const.IMG_THUMB).concat(ext);

        
        Runnable ftpTask = new FtpService(file, (), relativePath, fileName, ext,
                ip, port, userName, passWord);
        Thread t = new Thread(ftpTask);
        ();
        return ("文件上传成功", entity);
    }

    private boolean isPic(String ext) {
        String _ext = ().toLowerCase();
        if(_ext.equals(".png")||_ext.equals(".jpeg")||_ext.equals(".jpg")||_ext.equals(".bmp")){
            return true;
        }else{
            return false;
        }
    }

    @GetMapping("/showImage")
    public void showImage(
            @RequestParam(value = "filePath", required = true) String filePath,
            HttpServletResponse response){
        try {
            showFtpImage(ip, port, userName, passWord, filePath, response);
        } catch (Exception e) {
            ();
        }
    }

    @PostMapping("/deleteFileByPath")
    @ResponseBody
    public JsonResult deleteFileByPath(
            @RequestParam(value = "filePath", required = true) String filePath){
        delFtpImage(ip, port, userName, passWord, filePath);
        return ("图片删除成功", null);
    }

    private void uploadFileToFtp(String ip, int port, String userName, String passWord, File file, String fileName, boolean needDelete, String relativePath){
        try {
            FtpUtils ftpUtils = ();
            (ip, port, userName, passWord);
            (new FileInputStream(file), fileName, needDelete, relativePath);
        } catch (Exception e) {

        }
    }

    private void showFtpImage(String ip, int port, String userName, String passWord, String filePath, HttpServletResponse response){
        try {
            FtpUtils ftpUtils = ();
            FTPClient ftpClient = (ip, port, userName, passWord);
            (filePath, ());
        } catch (Exception e) {

        }
    }

    private void delFtpImage(String ip, int port, String userName, String passWord, String filePath){
        try {
            FtpUtils ftpUtils = ();
            FTPClient ftpClient = (ip, port, userName, passWord);
            (filePath);
        } catch (Exception e) {

        }
    }


}

上面类中分别引用了工具类 JsonResult、FtpUtils、DateUtils, 请看后面内容,会逐个描述出来

创建JsonResult工具类

package ;

import ;
import ;
import ;

import ;

@JsonInclude()
public class JsonResult implements Serializable {

    private String code;

    private String message;

    private Object data;

    private JsonResult(){

    }

    private static JsonResult buildJsonResult(String code, String message, Object data){
        JsonResult result = new JsonResult();
        (code);
        (message);
        (data);
        return result;
    }

    public static JsonResult ok(){
        return buildJsonResult("10000", "请求成功", null);
    }

    public static JsonResult ok(Object data){
        return buildJsonResult("10000", "请求成功", data);
    }

    public static JsonResult ok(String message, Object data){
        return buildJsonResult("10000", message, data);
    }

    public static JsonResult customize(String code, String message, Object data){
        return buildJsonResult(code, message, data);
    }

    public static JsonResult error(){
        return buildJsonResult(Const.ERROR_CODE, "请求失败", null);
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
         = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
         = message;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
         = data;
    }

    
}

创建Const公共属性类

package ;

public class Const {

    /**
     * 失败业务CODE
     */
    public final static String ERROR_CODE = "10001";

    /**
     * 统一异常CODE
     */
    public final static String WARN_CODE = "10002";

     /**
     * 图片(中图)标记
     */
    public final static String IMG_MIDDLE = "_middle";

    /**
     * 图片(缩略图)标记
     */
    public final static String IMG_THUMB = "_thumb";

    /**
     * 目录分隔符
     */
    public final static String SPER = "/";

}

创建DateUtils工具类

package ;

import ;

import ;
import ;
import ;
import ;
import ;
import ;

public class DateUtils {

    private static TimeZone shanghaiZone = ("Asia/Shanghai");

    public static String getDate(){
        Calendar cal = (shanghaiZone);
        return () + "/" + (() +1) + "/" + ();
    }

    public static String getLongDateString(){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String longdate = (new Date());
        return longdate;
    }

    public static String random(long len) throws Exception {
        String[] arr = new String[]{"0","1","2","3","4","5","6","7","8","9"};
        if(len<=0){
            throw new Exception("长度有误");
        }
        String suffix = "";
        for (int i=0; i<len; i++){
            Random r = new Random();
            int index = (10);
            if(index == 10) {
                index = 0;
            }
            suffix += arr[index];
        }
        return suffix;
    }

    public static Date getDate(String datestr) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        Date date = null;
        try {
            date = (datestr);
        } catch (ParseException e) {
            ();
        }
        return date;
    }

    public static Date getDate(String datestr, String pattern) {
        if((pattern)){
            pattern = "yyyy-MM-dd";
        }
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        Date date = null;
        try {
            date = (datestr);
        } catch (ParseException e) {
            ();
        }
        return date;
    }

    /**
     * 获取过期时间,年月日
     * @param curr 基点日期
     * @param expire 过期间隔
     * @param type 0、日 1、月 2、年
     * @return
     */
    public static Date calExpireDate(Date curr, int expire, int type) throws Exception {
        if(expire>0) {
            Calendar c = ();
            (curr);
            if (type == 1) {
                (, expire);
            } else if (type == 2) {
                (, expire);
            } else {
                (, expire);
            }
            return ();
        }else{
            throw new Exception("过期间隔必须大于零");
        }
    }

    public static Date getDateTime(String datestr, String pattern) {
        if((pattern)){
            pattern = "yyyy-MM-dd HH:mm:ss";
        }

        return getDate((".0", ""), pattern);
    }

    public static String getDateTime(Date date, String pattern){
        if((pattern)){
            pattern = "yyyy-MM-dd HH:mm:ss";
        }
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        return (date);
    }

    public static String getDate(Date date, String pattern){
        if((pattern)){
            pattern = "yyyy-MM-dd";
        }
        return getDateTime(date, pattern);
    }
}

创建FtpUtils工具类

package ;

import ;
import ;
import .*;
import org.;
import org.;

import ;
import ;
import ;
import ;
import ;

public class FtpUtils {

    private Logger logger = ();

    private static FtpUtils instance = null;

    private FtpUtils(){

    }

    public static FtpUtils getInstance(){
        instance = new FtpUtils();
        return instance;
    }

    // ftp客户端
    private FTPClient ftpClient = null;

    /**
     * 获取 Ftp连接对象
     * @param ip IP 地址
     * @param port 端口号
     * @param userName 账户
     * @param passWord 密码
     * @return
     * @throws IOException
     */
    public FTPClient getFTPClient(String ip, int port, String userName, String passWord) throws IOException {
        if (ftpClient==null) {
            int reply;
            try {
                ftpClient=new FTPClient();
                (ip,port);
                (userName,passWord);
                reply = ();
                if (!(reply)) {
                    ();
                }
                return ftpClient;
            }catch(Exception ex){
                throw ex;
            }
        }
        return null;
    }

    /**
     * 带点的
     * @param fileName
     * @return
     */
    public static String getExtention(String fileName) {
        int pos = (".");
        return (pos);
    }
    /**
     * 不带点
     * @param fileName
     * @return
     */
    public static String getNoPointExtention(String fileName) {
        int pos = (".");
        return (pos +1);
    }
    /**
     *
     * 功能:根据当前时间获取文件目录
     * @return String
     */
    public static String getDateDir(Date dateParam){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        return (dateParam);
    }

    /**
     * 设置传输文件的类型[文本文件或者二进制文件]
     *
     * @param fileType
     *            --BINARY_FILE_TYPE、ASCII_FILE_TYPE
     */
    private void setFileType(int fileType) {
        try {
            (fileType);
        } catch (Exception e) {

        }
    }

    /**
     *
     * 功能:关闭连接
     */
    public void closeConnect() {
        try {
            if (ftpClient != null) {
                ();
                ();
            }
        } catch (Exception e) {

        }
    }

    /**
     * 转码[GBK -> ISO-8859-1] 不同的平台需要不同的转码
     *
     * @param obj
     * @return
     */
    private String gbkToIso8859(Object obj) {
        try {
            if (obj == null)
                return "";
            else
                return new String(().getBytes("GBK"), "iso-8859-1");
        } catch (Exception e) {
            return "";
        }
    }

    /**
     *
     * 功能:上传文件附件到文件服务器
     * @param buffIn:上传文件流
     * @param fileName:保存文件名称
     * @param needDelete:是否同时删除
     * @return
     * @throws IOException
     */
    public boolean uploadToFtp(InputStream buffIn, String fileName, boolean needDelete, String relativePath)
            throws FTPConnectionClosedException, IOException,Exception {
        boolean returnValue = false;
        // 上传文件
        try {
            // 设置传输二进制文件
            setFileType(FTP.BINARY_FILE_TYPE);
            int reply = ();
            if(!(reply))
            {
                ();
                throw new IOException("Ftp连接失败");
            }
            ();
            // 支持动态创建多级目录
            createDir(relativePath);
            // 上传文件到ftp
            returnValue = (fileName, buffIn);
            if(needDelete){
                (fileName);
            }
            // 输出操作结果信息
            if (returnValue) {
                ("文件上传成功");
            } else {
                ("文件上传失败");
            }
            ();
            // 关闭连接
            closeConnect();
        } catch (Exception e) {
            returnValue = false;
            throw e;
        } finally {
            try {
                if (buffIn != null) {
                    ();
                }
            } catch (Exception e) {

            }
            if (()) {
                closeConnect();
            }
        }
        return returnValue;
    }

    /**
     * 创建目录(有则切换目录,没有则创建目录)
     * @param dir
     * @return
     */
    public boolean createDir(String dir){
        String d;
        try {
            //目录编码,解决中文路径问题
            d = new String(().getBytes("GBK"),"iso-8859-1");
            //尝试切入目录
            if((d))
                return true;
            String[] arr =  ();
            StringBuffer sbfDir=new StringBuffer();
            //循环生成子目录
            for(String s : arr){
                ();
                (s);
                //目录编码,解决中文路径问题
                d = new String(().getBytes("GBK"),"iso-8859-1");
                //尝试切入目录
                if((d))
                    continue;
                if(!(d)){
                    return false;
                }
            }
            //将目录切换至指定路径
            return (d);
        } catch (Exception e) {
            ();
            return false;
        }
    }



}

创建 FtpService 线程类,用于处理图片文件传至FTP服务器

package ;

import ;
import ;
import ;
import ;
import org.;
import org.;

import ;
import ;
import ;
import ;

public class FtpService implements Runnable {

    private Logger logger = ();

    /**
     * 物理路径
     */
    private String physicalPath;

    /**
     * 相对路径
     */
    private String relativePath;

    /**
     * 文件名称(重命名后)
     */
    private String fileName;

    /**
     * 扩展名
     */
    private String ext;

    private File file;

    private String ip;

    private int port;

    private String userName;

    private String passWord;

    public FtpService(
            File file, String physicalPath, String relativePath, String fileName, String ext,
            String ip, int port, String userName, String passWord) {
         = file;
         = physicalPath;
         = relativePath;
         = fileName;
         = ext;
         = ip;
         = port;
         = userName;
         = passWord;
    }

    public FtpService() {
    }

    @Override
    public void run() {
        try {

            // 上传至FTP
            uploadFileToFtp(ip, port, userName, passWord, file, (ext), false, relativePath);

            // 生产中图
            String middle = ().concat(fileName).concat(Const.IMG_MIDDLE).concat(ext);
            (file)
                    .scale(0.5)
                    .outputQuality(0.8)
                    .toFile(middle);

            File fileMiddle = new File(middle);

            // 上传至FTP
            uploadFileToFtp(ip, port, userName, passWord, fileMiddle, (Const.IMG_MIDDLE).concat(ext), false, relativePath);

            // 生产缩略图
            String thumb = ().concat(fileName).concat(Const.IMG_THUMB).concat(ext);
            (file)
                    .scale(0.25)
                    .outputQuality(0.8)
                    .toFile(thumb);

            File fileThumb = new File(thumb);

            // 上传至FTP
            uploadFileToFtp(ip, port, userName, passWord, fileThumb, (Const.IMG_THUMB).concat(ext), false, relativePath);

            // 删除临时文件
            ();
            ();
            ();
        } catch (IOException e) {
            ();

        }
    }

    protected void uploadFileToFtp(String ip, int port, String userName, String passWord, File file, String fileName, boolean needDelete, String relativePath){
        try {
            FtpUtils ftpUtils = ();
            (ip, port, userName, passWord);
            (new FileInputStream(file), fileName, needDelete, relativePath);
        } catch (Exception e) {

        }
    }

    protected void showFtpImage(String ip, int port, String userName, String passWord, String filePath, HttpServletResponse response){
        try {
            FtpUtils ftpUtils = ();
            FTPClient ftpClient = (ip, port, userName, passWord);
            (filePath, ());
        } catch (Exception e) {

        }
    }

    protected void delFtpImage(String ip, int port, String userName, String passWord, String filePath){
        try {
            FtpUtils ftpUtils = ();
            FTPClient ftpClient = (ip, port, userName, passWord);
            // 如果路径中包含中文,下面代码必定要写上
            String tmpfilePath = new String(("GBK"),"iso-8859-1");
            (tmpfilePath);
        } catch (Exception e) {

        }
    }
}

创建DelService线程类,用于处理删除FTP服务器上的图片

package ;

import ;
import ;
import ;
import ;
import ;

public class DelService implements Runnable {

    private String ip;

    private int port;

    private String userName;

    private String passWord;

    private String delPath;

    public DelService() {
    }

    public DelService(String ip, int port, String userName, String passWord, String delPath) {
         = ip;
         = port;
         = userName;
         = passWord;
         = delPath;
    }

    @Override
    public void run() {
        // 如果删除图片路径不为空,执行删除操作
        if ((delPath)) {
            if (("showImage") > 0) {
                String[] arr = ("=");
                String tmpExt = (arr[1]);
                // 将要被删除的原图地址
                String tmpfilePath = arr[1].replace(Const.IMG_MIDDLE, "").replace(Const.IMG_THUMB, "");
                delFtpImage(ip, port, userName, passWord, tmpfilePath);
                // 将要被删除的中图地址
                String tmpFileMiddle = (tmpExt, "").concat(Const.IMG_MIDDLE).concat(tmpExt);
                delFtpImage(ip, port, userName, passWord, tmpFileMiddle);
                // 将要删除的缩略图地址
                String tmpFileThumb = (tmpExt, "").concat(Const.IMG_THUMB).concat(tmpExt);
                delFtpImage(ip, port, userName, passWord, tmpFileThumb);

            } else {
                delFtpImage(ip, port, userName, passWord, delPath);
            }
        }
    }


    protected void delFtpImage(String ip, int port, String userName, String passWord, String filePath){
        try {
            FtpUtils ftpUtils = ();
            FTPClient ftpClient = (ip, port, userName, passWord);
            // 如果路径中包含中文,下面代码必定要写上
            String tmpfilePath = new String(("GBK"),"iso-8859-1");
            (tmpfilePath);
        } catch (Exception e) {

        }
    }
}

相关的示例代码,均在上面描述中,采用FTP方式处理图片的存储,Controller在接收到Base64数据之后,会在当前服务器上转化为原始图片,然后交给FtpService 线程类进行后续处理,线程类负责,将原始图片再另外生成 中图和缩略图,最后将这三张图片同时传至FTP进行存储,并在传输成功之后,删除应用服务器上临时生成的这三张图片。

 

 

 

相关文章