引入依赖
commons-net
commons-net
3.6
net.coobird
thumbnailator
0.4.8
net.sf.json-lib
json-lib
2.4
jdk15
创建 Base64 工具类
package com.lill.test.utils;
import org.apache.commons.net.util.Base64;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import java.io.*;
public class BaseUtils {
static BASE64Encoder encoder = new sun.misc.BASE64Encoder();
static BASE64Decoder decoder = new sun.misc.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 = bin.read(buffer);
while (len != -1) {
bout.write(buffer, 0, len);
len = bin.read(buffer);
}
// 刷新此输出流并强制写出所有缓冲的输出字节,必须这行代码,否则有可能有问题
bout.flush();
byte[] bytes = baos.toByteArray();
return Base64.encodeBase64String(bytes).trim();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fin.close();
bin.close();
// 关闭 ByteArrayOutputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException
// IOException
// baos.close();
bout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/**
* 将base64编码转换成文件
*/
public static void base64StringToFile(String base64sString, String fileFullName) {
BufferedInputStream bin = null;
FileOutputStream fout = null;
BufferedOutputStream bout = null;
try {
// 将base64编码的字符串解码成字节数组
byte[] bytes = decoder.decodeBuffer(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 = bin.read(buffers);
while (len != -1) {
bout.write(buffers, 0, len);
len = bin.read(buffers);
}
// 刷新此输出流并强制写出所有缓冲的输出字节,必须这行代码,否则有可能有问题
bout.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bin.close();
fout.close();
bout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static InputStream base64StrToInputStream(String base64string) {
ByteArrayInputStream stream = null;
try {
BASE64Decoder decoder = new BASE64Decoder();
byte[] bytes1 = decoder.decodeBuffer(base64string);
stream = new ByteArrayInputStream(bytes1);
} catch (Exception e) {
e.printStackTrace();
}
return stream;
}
}
解除上传文件大小限制,在application.yml文件中做下简单配置
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),需要对server.xml做下修改
Nginx下文件上传大小,需要对nginx.conf做下修改
http{
……
client_max_body_size 200M;
……
}
设置完大小限制之后,都要重新启动下,使其生效
创建上传ImageController类
package com.lill.test.controller;
import com.lill.test.common.Const;
import com.lill.test.dao.IFileParameterRepository;
import com.lill.test.entity.FileParameter;
import com.lill.test.threads.DelService;
import com.lill.test.threads.FtpService;
import com.lill.test.utils.*;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.util.Date;
@Controller
@RequestMapping("/image")
@CrossOrigin(origins = "*", maxAge = 3600)
public class ImageController {
// 图片上传存储的路径
@Value("${upload.image.url}")
private String imageDir;
@Value("${upload.file.url}")
private String fileDir;
private Logger logger = LoggerFactory.getLogger(ImageController.class);
/**
* FTP 相关配置
*/
@Value("${ftp.username}")
private String userName;
@Value("${ftp.password}")
private String passWord;
@Value("${ftp.host}")
private String ip;
@Value("${ftp.port}")
private int port;
@Value("${ftp.filepath}")
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(StringUtils.isBlank(fileBase)){
return JsonResult.customize(Const.ERROR_CODE, "请先选择一张图片上传", null);
}
try {
// 执行删除程序
DelService delTask = new DelService(ip, port, userName, passWord, delPath);
Thread d = new Thread(delTask);
d.start();
}catch (Exception e){
}
String ext = ".png";
String fileName = GuidUtils.toLower();
// 创建目录
String relativePath = type.concat(Const.SPER).concat(DateUtils.getDate(new Date(), "yyyy/MM/dd"));
File dir = new File(imageDir.concat(Const.SPER)+ relativePath);
if(!dir.exists()){
dir.mkdirs();
}
String newFileBase = "";
if(fileBase.indexOf(",")>1) {
newFileBase = fileBase.substring(fileBase.indexOf(",") + 1);
}else{
newFileBase = fileBase;
}
// 生产原图
String normal = dir.getPath().concat(Const.SPER).concat(fileName).concat(ext);
BaseUtils.base64StringToFile(newFileBase, normal);
File file = new File(normal);
// 生产中图
String middle = dir.getPath().concat(Const.SPER).concat(fileName).concat(Const.IMG_MIDDLE).concat(ext);
// 生产缩略图
String thumb = dir.getPath().concat(Const.SPER).concat(fileName).concat(Const.IMG_THUMB).concat(ext);
Runnable ftpTask = new FtpService(file, dir.getPath(), relativePath, fileName, ext,
ip, port, userName, passWord);
Thread t = new Thread(ftpTask);
t.start();
return JsonResult.ok("文件上传成功", entity);
}
private boolean isPic(String ext) {
String _ext = ext.trim().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) {
e.printStackTrace();
}
}
@PostMapping("/deleteFileByPath")
@ResponseBody
public JsonResult deleteFileByPath(
@RequestParam(value = "filePath", required = true) String filePath){
delFtpImage(ip, port, userName, passWord, filePath);
return JsonResult.ok("图片删除成功", null);
}
private void uploadFileToFtp(String ip, int port, String userName, String passWord, File file, String fileName, boolean needDelete, String relativePath){
try {
FtpUtils ftpUtils = FtpUtils.getInstance();
ftpUtils.getFTPClient(ip, port, userName, passWord);
ftpUtils.uploadToFtp(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 = FtpUtils.getInstance();
FTPClient ftpClient = ftpUtils.getFTPClient(ip, port, userName, passWord);
ftpClient.retrieveFile(filePath, response.getOutputStream());
} catch (Exception e) {
}
}
private void delFtpImage(String ip, int port, String userName, String passWord, String filePath){
try {
FtpUtils ftpUtils = FtpUtils.getInstance();
FTPClient ftpClient = ftpUtils.getFTPClient(ip, port, userName, passWord);
ftpClient.deleteFile(filePath);
} catch (Exception e) {
}
}
}
上面类中分别引用了工具类 JsonResult、FtpUtils、DateUtils, 请看后面内容,会逐个描述出来
创建JsonResult工具类
package com.lill.test.utils;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.lill.test.common.Const;
import java.io.Serializable;
@JsonInclude(JsonInclude.Include.ALWAYS)
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();
result.setCode(code);
result.setMessage(message);
result.setData(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) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
创建Const公共属性类
package com.lill.test.common;
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 com.lill.test.utils;
import org.apache.commons.lang.StringUtils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;
import java.util.TimeZone;
public class DateUtils {
private static TimeZone shanghaiZone = TimeZone.getTimeZone("Asia/Shanghai");
public static String getDate(){
Calendar cal = Calendar.getInstance(shanghaiZone);
return cal.get(Calendar.YEAR) + "/" + (cal.get(Calendar.MONTH) +1) + "/" + cal.get(Calendar.DATE);
}
public static String getLongDateString(){
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
String longdate = sdf.format(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; i0) {
Calendar c = Calendar.getInstance();
c.setTime(curr);
if (type == 1) {
c.add(Calendar.MONTH, expire);
} else if (type == 2) {
c.add(Calendar.YEAR, expire);
} else {
c.add(Calendar.DATE, expire);
}
return c.getTime();
}else{
throw new Exception("过期间隔必须大于零");
}
}
public static Date getDateTime(String datestr, String pattern) {
if(StringUtils.isBlank(pattern)){
pattern = "yyyy-MM-dd HH:mm:ss";
}
return getDate(datestr.replace(".0", ""), pattern);
}
public static String getDateTime(Date date, String pattern){
if(StringUtils.isBlank(pattern)){
pattern = "yyyy-MM-dd HH:mm:ss";
}
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
return sdf.format(date);
}
public static String getDate(Date date, String pattern){
if(StringUtils.isBlank(pattern)){
pattern = "yyyy-MM-dd";
}
return getDateTime(date, pattern);
}
}
创建FtpUtils工具类
package com.lill.test.utils;
import com.lill.test.common.Const;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.net.ftp.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class FtpUtils {
private Logger logger = LoggerFactory.getLogger(FtpUtils.class);
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();
ftpClient.connect(ip,port);
ftpClient.login(userName,passWord);
reply = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftpClient.disconnect();
}
return ftpClient;
}catch(Exception ex){
throw ex;
}
}
return null;
}
/**
* 带点的
* @param fileName
* @return
*/
public static String getExtention(String fileName) {
int pos = fileName.lastIndexOf(".");
return fileName.substring(pos);
}
/**
* 不带点
* @param fileName
* @return
*/
public static String getNoPointExtention(String fileName) {
int pos = fileName.lastIndexOf(".");
return fileName.substring(pos +1);
}
/**
*
* 功能:根据当前时间获取文件目录
* @return String
*/
public static String getDateDir(Date dateParam){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
return sdf.format(dateParam);
}
/**
* 设置传输文件的类型[文本文件或者二进制文件]
*
* @param fileType
* --BINARY_FILE_TYPE、ASCII_FILE_TYPE
*/
private void setFileType(int fileType) {
try {
ftpClient.setFileType(fileType);
} catch (Exception e) {
}
}
/**
*
* 功能:关闭连接
*/
public void closeConnect() {
try {
if (ftpClient != null) {
ftpClient.logout();
ftpClient.disconnect();
}
} catch (Exception e) {
}
}
/**
* 转码[GBK -> ISO-8859-1] 不同的平台需要不同的转码
*
* @param obj
* @return
*/
private String gbkToIso8859(Object obj) {
try {
if (obj == null)
return "";
else
return new String(obj.toString().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 = ftpClient.getReplyCode();
if(!FTPReply.isPositiveCompletion(reply))
{
ftpClient.disconnect();
throw new IOException("Ftp连接失败");
}
ftpClient.enterLocalPassiveMode();
// 支持动态创建多级目录
createDir(relativePath);
// 上传文件到ftp
returnValue = ftpClient.storeFile(fileName, buffIn);
if(needDelete){
ftpClient.deleteFile(fileName);
}
// 输出操作结果信息
if (returnValue) {
System.out.println("文件上传成功");
} else {
System.out.println("文件上传失败");
}
buffIn.close();
// 关闭连接
closeConnect();
} catch (Exception e) {
returnValue = false;
throw e;
} finally {
try {
if (buffIn != null) {
buffIn.close();
}
} catch (Exception e) {
}
if (ftpClient.isConnected()) {
closeConnect();
}
}
return returnValue;
}
/**
* 创建目录(有则切换目录,没有则创建目录)
* @param dir
* @return
*/
public boolean createDir(String dir){
String d;
try {
//目录编码,解决中文路径问题
d = new String(dir.toString().getBytes("GBK"),"iso-8859-1");
//尝试切入目录
if(ftpClient.changeWorkingDirectory(d))
return true;
String[] arr = dir.split(Const.SPER);
StringBuffer sbfDir=new StringBuffer();
//循环生成子目录
for(String s : arr){
sbfDir.append(Const.SPER);
sbfDir.append(s);
//目录编码,解决中文路径问题
d = new String(sbfDir.toString().getBytes("GBK"),"iso-8859-1");
//尝试切入目录
if(ftpClient.changeWorkingDirectory(d))
continue;
if(!ftpClient.makeDirectory(d)){
return false;
}
}
//将目录切换至指定路径
return ftpClient.changeWorkingDirectory(d);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
创建 FtpService 线程类,用于处理图片文件传至FTP服务器
package com.lill.test.threads;
import com.lill.test.common.Const;
import com.lill.test.utils.FtpUtils;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.net.ftp.FTPClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class FtpService implements Runnable {
private Logger logger = LoggerFactory.getLogger(FtpService.class);
/**
* 物理路径
*/
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) {
this.file = file;
this.physicalPath = physicalPath;
this.relativePath = relativePath;
this.fileName = fileName;
this.ext = ext;
this.ip = ip;
this.port = port;
this.userName = userName;
this.passWord = passWord;
}
public FtpService() {
}
@Override
public void run() {
try {
// 上传至FTP
uploadFileToFtp(ip, port, userName, passWord, file, fileName.concat(ext), false, relativePath);
// 生产中图
String middle = physicalPath.concat(Const.SPER).concat(fileName).concat(Const.IMG_MIDDLE).concat(ext);
Thumbnails.of(file)
.scale(0.5)
.outputQuality(0.8)
.toFile(middle);
File fileMiddle = new File(middle);
// 上传至FTP
uploadFileToFtp(ip, port, userName, passWord, fileMiddle, fileName.concat(Const.IMG_MIDDLE).concat(ext), false, relativePath);
// 生产缩略图
String thumb = physicalPath.concat(Const.SPER).concat(fileName).concat(Const.IMG_THUMB).concat(ext);
Thumbnails.of(file)
.scale(0.25)
.outputQuality(0.8)
.toFile(thumb);
File fileThumb = new File(thumb);
// 上传至FTP
uploadFileToFtp(ip, port, userName, passWord, fileThumb, fileName.concat(Const.IMG_THUMB).concat(ext), false, relativePath);
// 删除临时文件
file.delete();
fileMiddle.delete();
fileThumb.delete();
} catch (IOException e) {
e.printStackTrace();
}
}
protected void uploadFileToFtp(String ip, int port, String userName, String passWord, File file, String fileName, boolean needDelete, String relativePath){
try {
FtpUtils ftpUtils = FtpUtils.getInstance();
ftpUtils.getFTPClient(ip, port, userName, passWord);
ftpUtils.uploadToFtp(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 = FtpUtils.getInstance();
FTPClient ftpClient = ftpUtils.getFTPClient(ip, port, userName, passWord);
ftpClient.retrieveFile(filePath, response.getOutputStream());
} catch (Exception e) {
}
}
protected void delFtpImage(String ip, int port, String userName, String passWord, String filePath){
try {
FtpUtils ftpUtils = FtpUtils.getInstance();
FTPClient ftpClient = ftpUtils.getFTPClient(ip, port, userName, passWord);
// 如果路径中包含中文,下面代码必定要写上
String tmpfilePath = new String(filePath.getBytes("GBK"),"iso-8859-1");
ftpClient.deleteFile(tmpfilePath);
} catch (Exception e) {
}
}
}
创建DelService线程类,用于处理删除FTP服务器上的图片
package com.lill.test.threads;
import com.lill.test.common.Const;
import com.lill.test.utils.FtpUtils;
import com.lill.test.utils.NameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
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) {
this.ip = ip;
this.port = port;
this.userName = userName;
this.passWord = passWord;
this.delPath = delPath;
}
@Override
public void run() {
// 如果删除图片路径不为空,执行删除操作
if (StringUtils.isNotBlank(delPath)) {
if (delPath.indexOf("showImage") > 0) {
String[] arr = delPath.split("=");
String tmpExt = NameUtils.getFileExtension(arr[1]);
// 将要被删除的原图地址
String tmpfilePath = arr[1].replace(Const.IMG_MIDDLE, "").replace(Const.IMG_THUMB, "");
delFtpImage(ip, port, userName, passWord, tmpfilePath);
// 将要被删除的中图地址
String tmpFileMiddle = tmpfilePath.replace(tmpExt, "").concat(Const.IMG_MIDDLE).concat(tmpExt);
delFtpImage(ip, port, userName, passWord, tmpFileMiddle);
// 将要删除的缩略图地址
String tmpFileThumb = tmpfilePath.replace(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 = FtpUtils.getInstance();
FTPClient ftpClient = ftpUtils.getFTPClient(ip, port, userName, passWord);
// 如果路径中包含中文,下面代码必定要写上
String tmpfilePath = new String(filePath.getBytes("GBK"),"iso-8859-1");
ftpClient.deleteFile(tmpfilePath);
} catch (Exception e) {
}
}
}
相关的示例代码,均在上面描述中,采用FTP方式处理图片的存储,Controller在接收到Base64数据之后,会在当前服务器上转化为原始图片,然后交给FtpService 线程类进行后续处理,线程类负责,将原始图片再另外生成 中图和缩略图,最后将这三张图片同时传至FTP进行存储,并在传输成功之后,删除应用服务器上临时生成的这三张图片。