SpringMVC文件上傳的三種方法
此文章將介紹使用SpringMVC進行commons-fileupload、FTP、aliyunOSS三種文件上傳和下載的案例。文件上傳的解決方案還有很多,例如阿里的fastDFS等。這里就不做介紹了,有興趣的小伙伴可以自行baidu學習~
搭建項目
案例的pom依賴,其中commons-fileupload、aliyun、ftp可以根據自己的需要選擇是否導入
<!-- 編碼和jdk -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- spring相關jar包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!--servlet相關-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--json相關-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.5</version>
</dependency>
<!--文件上傳-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3</version>
</dependency>
<!--引入FTP 依賴-->
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
<!-- 阿里雲OSS -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.8.0</version>
</dependency>
<!--日志相關-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!--二維碼依賴-->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
<build>
<!--插件-->
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<path>/</path>
<port>8080</port>
<server>tomcat7</server>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
logback配置文件,在resources文件夾下創建logback.xml文件,拷貝入如下代碼,這里指定了日志輸出路徑為項目的根目錄。
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定義日志文件的存儲地址 logs為當前項目的logs目錄 還可以設置為../logs -->
<property name="LOG_HOME" value="logs" />
<!--控制台日志, 控制台輸出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度,%msg:日志消息,%n是換行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!--文件日志, 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件輸出的文件名-->
<FileNamePattern>${LOG_HOME}/TestWeb.log.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天數-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級別從左顯示5個字符寬度%msg:日志消息,%n是換行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!-- show parameters for hibernate sql 專為 Hibernate 定制 -->
<logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE" />
<logger name="org.hibernate.type.descriptor.sql.BasicExtractor" level="DEBUG" />
<logger name="org.hibernate.SQL" level="DEBUG" />
<logger name="org.hibernate.engine.QueryParameters" level="DEBUG" />
<logger name="org.hibernate.engine.query.HQLQueryPlan" level="DEBUG" />
<!--myibatis log configure-->
<logger name="com.apache.ibatis" level="TRACE"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<!-- 日志輸出級別 -->
<root level="DEBUG">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE"/>
</root>
</configuration>
配置aliyun.properties文件,同樣在resources文件夾下創建此文件,內容如下,如果不需要使用aliyunOSS可以忽略。
aliyun.AccessKeyID=yourAccessKeyID
aliyun.AccessKeySecret=yourAccessKeySecret
aliyun.Buckets=yourBuckets
aliyun.EndPoint=https://oss-cn-beijing.aliyuncs.com
aliyun.prefix=prefix/
配置SpringMVC文件,在resources目錄下創建一個名為spring的文件夾,創建Spring-MVC.xml粘貼如下內容
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
<!--掃描包-->
<context:component-scan base-package="cn.rayfoo"/>
<!--放行靜態資源-->
<mvc:default-servlet-handler/>
<!--注解驅動-->
<!-- 注冊MVC注解驅動 -->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<!--json編碼轉化-->
<bean class="org.springframework.http.converter.StringHttpMessageConverter" >
<property name = "supportedMediaTypes">
<list>
<value>text/html;charset=utf-8</value>
<value>application/json;charset=utf-8</value>
</list>
</property>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" >
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!--文件解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--文件最大可上傳大小,單位byte 此處為10M 1024*1024*10-->
<property name="maxUploadSize" value="10485760"></property>
</bean>
</beans>
配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--請求編碼過濾器-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--設置編碼格式-->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!--設置請求和響應是否強制編碼-->
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置前端控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置有請求訪問時加載的文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/Spring-MVC.xml</param-value>
</init-param>
<!--配置自動啟動前端控制器-->
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
cn.rayfoo.common包中,添加了一個枚舉,用於指定協議的類型
package cn.rayfoo.common;
/**
* @Author: rayfoo@qq.com
* @Date: 2020/7/21 10:46 上午
* @Description: 封裝http和https
*/
public enum Protocol {
SECURITY_HTTP("https://"),
COMMON_HTTP("http://");
private String protocolType;
public String getProtocolType() {
return protocolType;
}
public void setProtocolType(String protocolType) {
this.protocolType = protocolType;
}
Protocol(String protocolType) {
this.protocolType = protocolType;
}
}
cn.rayfoo.utils中使用了四個工具類,其中AliyunOSSUtils、PropertiesReader用於阿里雲OSS,QRCodeUtil為二維碼生成,FtpUtil為ftp上傳和下載。
PropertiesReader
package cn.rayfoo.utils;
import java.io.InputStream;
import java.util.Properties;
/**
* Created by rayfoo@qq.com Luna on 2020/4/15 18:38
* Description : 讀取配置文件工具類
*/
public class PropertiesReader {
//創建Properties對象
private static Properties property = new Properties();
//在靜態塊中加載資源
static {
//使用try(){}.. 獲取數據源
//注意 * 這是jdk1.7開始支持的特性,如果使用的是低版本 需要提升jdk版本 或者更改寫法
try (
InputStream in = PropertiesReader.class.getResourceAsStream("/aliyun.properties");
) {
property.load(in);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 返回Properties對象
* @return
*/
public static Properties getProperties(){
return property;
}
/**
* 獲取字符串類型的值
* @param key
* @return
*/
public static String get(String key) {
return property.getProperty(key);
}
/**
* 獲取Integer類型的值
* @param key
* @return
*/
public static Integer getInteger(String key) {
String value = get(key);
return null == value ? null : Integer.valueOf(value);
}
/**
* 獲取Boolean類型的值
* @param key
* @return
*/
public static Boolean getBoolean(String key) {
String value = get(key);
return null == value ? null : Boolean.valueOf(value);
}
/**
* 設置一個鍵值對
* @param key
* @param value
*/
public static void set(String key,String value){
property.setProperty(key,value);
}
/**
* 添加一個鍵值對
* @param key
* @param value
*/
public static void add(String key,Object value){
property.put(key,value);
}
}
AliyunOSSUtil
package cn.rayfoo.utils;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
/**
* @Author: rayfoo@qq.com
* @Date: 2020/7/13 3:11 下午
* @Description:
*/
public class AliyunOSSUtil {
/**
* 阿里雲的配置參數
*/
private static String accessKeyId = null;
private static String accessKeySecret = null;
private static String endpoint = null;
private static String bucketName = null;
/**
* 存儲在OSS中的前綴名
*/
private static String file_prefix = null;
/**
* 靜態塊
*/
static {
//初始化AccessKey
accessKeyId = PropertiesReader.get("aliyun.AccessKeyID");
//初始化AccessKeySecret
accessKeySecret = PropertiesReader.get("aliyun.AccessKeySecret");
//初始化Endpoint
endpoint = PropertiesReader.get("aliyun.EndPoint");
//初始化bucketName
bucketName = PropertiesReader.get("aliyun.Buckets");
//初始化前綴
file_prefix = PropertiesReader.get("aliyun.prefix");
}
/**
* 私有化構造
*/
private AliyunOSSUtils() {
}
/**
* 獲取圖片的URL頭信息
*
* @return 返回url頭信息
*/
private static String getURLHead() {
//從哪個位置截取
int cutPoint = endpoint.lastIndexOf('/') + 1;
//http頭
String head = endpoint.substring(0, cutPoint);
//服務器地址信息
String tail = endpoint.substring(cutPoint);
//返回結果
return head + bucketName + "." + tail + "/";
}
/**
* 獲取存儲在服務器上的地址
*
* @param oranName 文件名
* @return 文件URL
*/
private static String getRealName(String oranName) {
return getURLHead() + oranName;
}
/**
* 獲取一個隨機的文件名
*
* @param oranName 初始的文件名
* @return 返回加uuid后的文件名
*/
private static String getRandomImageName(String oranName) {
//獲取一個uuid 去掉-
String uuid = UUID.randomUUID().toString().replace("-", "");
//查一下是否帶路徑
int cutPoint = oranName.lastIndexOf("/") + 1;
//如果存在路徑
if (cutPoint != 0) {
//掐頭 如果開頭是/ 則去掉
String head = oranName.indexOf("/") == 0 ? oranName.substring(1, cutPoint) : oranName.substring(0, cutPoint);
//去尾
String tail = oranName.substring(cutPoint);
//返回正確的帶路徑的圖片名稱
return file_prefix + head + uuid + tail;
}
//不存在 直接返回
return file_prefix + uuid + oranName;
}
/**
* MultipartFile2File
* @param multipartFile
* @return
*/
private static File transferToFile(MultipartFile multipartFile) {
//選擇用緩沖區來實現這個轉換即使用java 創建的臨時文件 使用 MultipartFile.transferto()方法 。
File file = null;
try {
//獲取文件名
String originalFilename = multipartFile.getOriginalFilename();
//獲取最后一個"."的位置
int cutPoint = originalFilename.lastIndexOf(".");
//獲取文件名
String prefix = originalFilename.substring(0,cutPoint);
//獲取后綴名
String suffix = originalFilename.substring(cutPoint + 1);
//創建臨時文件
file = File.createTempFile(prefix, suffix);
//multipartFile2file
multipartFile.transferTo(file);
//刪除臨時文件
file.deleteOnExit();
} catch (IOException e) {
e.printStackTrace();
}
return file;
}
/**
* 上傳文件流
*
* @param oranFileName 上傳到服務器上的文件路徑和名稱
* @param file 來自本地的文件或者文件流
*/
public static String uploadFileInputSteam(String oranFileName, MultipartFile file) {
// <yourObjectName>上傳文件到OSS時需要指定包含文件后綴在內的完整路徑,例如abc/efg/123.jpg
String objectName = getRandomImageName(oranFileName);
// 創建OSSClient實例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// 上傳文件流
try (InputStream inputStream = new FileInputStream(transferToFile(file))) {
//上傳到OSS
ossClient.putObject(bucketName, objectName, inputStream);
} catch (Exception ex) {
ex.printStackTrace();
}
// 關閉OSSClient。
ossClient.shutdown();
//返回文件在服務器上的全路徑+名稱
return getRealName(objectName);
}
/**
* 上傳文件流
*
* @param oranFileName 上傳到服務器上的文件路徑和名稱
* @param file 來自本地的文件或者文件流
*/
public static String uploadFileInputSteam(String oranFileName, File file) {
// <yourObjectName>上傳文件到OSS時需要指定包含文件后綴在內的完整路徑,例如abc/efg/123.jpg
String objectName = getRandomImageName(oranFileName);
// 創建OSSClient實例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// 上傳文件流。
try (InputStream inputStream = new FileInputStream(file);) {
//上傳到OSS
ossClient.putObject(bucketName, objectName, inputStream);
} catch (Exception ex) {
ex.printStackTrace();
}
// 關閉OSSClient。
ossClient.shutdown();
//返回文件在服務器上的全路徑+名稱
return getRealName(objectName);
}
}
QRCodeUtil
package cn.rayfoo.utils;
import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Hashtable;
/**
* @Author: rayfoo@qq.com
* @Date: 2020/7/13 5:20 下午
* @Description: 二維碼工具類
*/
public class QRCodeUtil {
//編碼
private static final String CHARSET = "utf-8";
//文件格式
private static final String FORMAT_NAME = "JPG";
// 二維碼尺寸
private static final int QRCODE_SIZE = 300;
// LOGO寬度
private static final int WIDTH = 60;
// LOGO高度
private static final int HEIGHT = 60;
/**
* 生成二維碼
* @param content 內容
* @param imgPath logo
* @param needCompress 是否需要壓縮
* @return java.awt.image.BufferedImage
* @throws Exception
*/
private static BufferedImage createImage(String content, String imgPath, boolean needCompress) throws Exception {
Hashtable hints = new Hashtable();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
hints.put(EncodeHintType.MARGIN, 1);
BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE,
hints);
int width = bitMatrix.getWidth();
int height = bitMatrix.getHeight();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
}
}
if (imgPath == null || "".equals(imgPath)) {
return image;
}
// 插入圖片
QRCodeUtil.insertImage(image, imgPath, needCompress);
return image;
}
/**
* 插入logo
* @param source 二維碼圖片
* @param imgPath logo路徑
* @param needCompress 是否壓縮
* @throws Exception
*/
private static void insertImage(BufferedImage source, String imgPath, boolean needCompress) throws Exception {
File file = new File(imgPath);
if (!file.exists()) {
System.err.println("" + imgPath + " 該文件不存在!");
return;
}
Image src = ImageIO.read(new File(imgPath));
int width = src.getWidth(null);
int height = src.getHeight(null);
if (needCompress) { // 壓縮LOGO
if (width > WIDTH) {
width = WIDTH;
}
if (height > HEIGHT) {
height = HEIGHT;
}
Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);
BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics();
g.drawImage(image, 0, 0, null); // 繪制縮小后的圖
g.dispose();
src = image;
}
// 插入LOGO
Graphics2D graph = source.createGraphics();
int x = (QRCODE_SIZE - width) / 2;
int y = (QRCODE_SIZE - height) / 2;
graph.drawImage(src, x, y, width, height, null);
Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
graph.setStroke(new BasicStroke(3f));
graph.draw(shape);
graph.dispose();
}
public static void mkdirs(String destPath) {
File file = new File(destPath);
// 當文件夾不存在時,mkdirs會自動創建多層目錄,區別於mkdir.(mkdir如果父目錄不存在則會拋出異常)
if (!file.exists() && !file.isDirectory()) {
file.mkdirs();
}
}
/**
* 生成二維碼,輸出到指定路徑
* @param content 內容
* @param imgPath logo路徑
* @param destPath 輸出路徑
* @param needCompress 是否壓縮
* @throws Exception
*/
public static void encode(String content, String imgPath, String destPath, boolean needCompress) throws Exception {
BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);
mkdirs(destPath);
// String file = new Random().nextInt(99999999)+".jpg";
// ImageIO.write(image, FORMAT_NAME, new File(destPath+"/"+file));
ImageIO.write(image, FORMAT_NAME, new File(destPath));
}
/**
* 生成二維碼,獲得到輸入流 log內嵌
* @param content 內容
* @param imgPath logo路徑
* @param output 輸入流
* @param needCompress 是否壓縮
* @throws Exception
*/
public static void encode(String content, String imgPath, OutputStream output, boolean needCompress)
throws Exception {
BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);
ImageIO.write(image, FORMAT_NAME, output);
}
/**
* 獲取指定的logo文件輸入流
* @param logoPath logo路徑
* @return java.io.InputStream
*/
public static InputStream getResourceAsStream(String logoPath) {
return QRCodeUtil.class.getResourceAsStream(logoPath);
}
/**
* 將要解析的二維碼的存放路徑
* @param file 要解析的二維碼
* @return
* @throws Exception
*/
public static String decode(File file) throws Exception {
BufferedImage image;
image = ImageIO.read(file);
if (image == null) {
return null;
}
BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Result result;
Hashtable hints = new Hashtable();
hints.put(DecodeHintType.CHARACTER_SET, CHARSET);
result = new MultiFormatReader().decode(bitmap, hints);
String resultStr = result.getText();
return resultStr;
}
/**
* 解析二維碼
* @param path 要解析的二維碼路徑
* @return
* @throws Exception
*/
public static String decode(String path) throws Exception {
return QRCodeUtil.decode(new File(path));
}
}
FtpUtil
package cn.rayfoo.utils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import java.io.*;
/**
* @Author: rayfoo@qq.com
* @Date: 2020/7/21 9:01 上午
* @Description:
*/
public class FtpUtil {
/**
* FTP服務器hostname
*/
private static String HOST = "59.110.218.81";
/**
* FTP服務器端口
*/
private static int PORT = 21;
/**
* FTP登錄賬號
*/
private static String USERNAME = "root";
/**
* FTP登錄密碼
*/
private static String PASSWORD = "root";
/**
* FTP服務器基礎目錄 可以不填
*/
private static String BASEPATH = "";
/**
* FTP客戶端
*/
private static FTPClient ftp;
/**
* @Description 初始化FTP客戶端
* @Author xw
* @Date 12:34 2020/2/5
* @Param []
* @return boolean
**/
public static boolean initFtpClient(){
ftp = new FTPClient();
int reply;
try {
// 連接FTP服務器
ftp.connect(HOST, PORT);
//登錄, 如果采用默認端口,可以使用ftp.connect(host)的方式直接連接FTP服務器
ftp.login(USERNAME, PASSWORD);
ftp.setBufferSize(10240);
//設置傳輸超時時間為60秒
ftp.setDataTimeout(600000);
//連接超時為60秒
ftp.setConnectTimeout(600000);
//FTP以二進制形式傳輸
ftp.setFileType(FTP.BINARY_FILE_TYPE);
reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
return false;
}
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
/**
* Description: 向FTP服務器上傳文件
* @param filePath FTP服務器文件存放路徑。例如分日期存放:/2015/01/01。文件的路徑為basePath+filePath
* @param filename 上傳到FTP服務器上的文件名
* @param input 本地要上傳的文件的 輸入流
* @return 成功返回true,否則返回false
*/
public static boolean uploadFile(String filePath, String filename, InputStream input) {
boolean result = false;
try {
filePath = new String(filePath.getBytes("GBK"),"iso-8859-1");
filename = new String(filename.getBytes("GBK"),"iso-8859-1");
if (!initFtpClient()){
return result;
};
//切換到上傳目錄
ftp.enterLocalPassiveMode();
if (!ftp.changeWorkingDirectory(BASEPATH+filePath)) {
//如果目錄不存在創建目錄
String[] dirs = filePath.split("/");
String tempPath = BASEPATH;
for (String dir : dirs) {
if (null == dir || "".equals(dir)){
continue;
}
tempPath += "/" + dir;
if (!ftp.changeWorkingDirectory(tempPath)) {
if (!ftp.makeDirectory(tempPath)) {
return result;
} else {
ftp.changeWorkingDirectory(tempPath);
}
}
}
}
//設置上傳文件的類型為二進制類型
ftp.setFileType(FTP.BINARY_FILE_TYPE);
//上傳文件
ftp.enterLocalPassiveMode();
if (!ftp.storeFile(filename, input)) {
return result;
}
input.close();
ftp.logout();
result = true;
}
catch (IOException e) {
e.printStackTrace();
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException ioe) {
}
}
}
return result;
}
/**
* Description: 從FTP服務器下載文件
* @param remotePath FTP服務器上的相對路徑
* @param fileName 要下載的文件名
* @return
*/
public static boolean downloadFile( String remotePath,String fileName,String localPath) {
boolean result = false;
try {
remotePath = new String(remotePath.getBytes("GBK"),"iso-8859-1");
fileName = new String(fileName.getBytes("GBK"),"iso-8859-1");
if (!initFtpClient()){
return result;
};
// 轉移到FTP服務器目錄
ftp.changeWorkingDirectory(remotePath);
ftp.enterLocalPassiveMode();
FTPFile[] fs = ftp.listFiles();
for (FTPFile ff : fs) {
if (ff.getName().equals(fileName)) {
ftp.enterLocalPassiveMode();
FileOutputStream outputStream = new FileOutputStream(new File(localPath));
ftp.retrieveFile(remotePath+"/"+fileName,outputStream);
result = true;
outputStream.close();
}
}
ftp.logout();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException ioe) {
}
}
}
return result;
}
/**
* @Description 從ftp服務器下載文件到指定輸出流
* @Author xw
* @Date 22:30 2020/3/5
* @Param [remotePath, fileName, outputStream]
* @return boolean
**/
public static boolean downloadFile(String remotePath, String fileName, OutputStream outputStream) {
boolean result = false;
try {
remotePath = new String(remotePath.getBytes("GBK"),"iso-8859-1");
fileName = new String(fileName.getBytes("GBK"),"iso-8859-1");
if (!initFtpClient()){
return result;
};
// 轉移到FTP服務器目錄
ftp.changeWorkingDirectory(remotePath);
ftp.enterLocalPassiveMode();
FTPFile[] fs = ftp.listFiles();
for (FTPFile ff : fs) {
if (ff.getName().equals(fileName)) {
ftp.enterLocalPassiveMode();
ftp.retrieveFile(remotePath+"/"+fileName,outputStream);
result = true;
}
}
ftp.logout();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException ioe) {
}
}
}
return result;
}
/**
* @Description 刪除文件
* @Author xw
* @Date 11:38 2020/2/6
* @Param [remotePath, fileName]
* @return void
**/
public static boolean deleteFile( String remotePath,String fileName){
boolean flag = false;
try {
remotePath = new String(remotePath.getBytes("GBK"),"iso-8859-1");
fileName = new String(fileName.getBytes("GBK"),"iso-8859-1");
if (!initFtpClient()){
return flag;
};
// 轉移到FTP服務器目錄
ftp.changeWorkingDirectory(remotePath);
ftp.enterLocalPassiveMode();
FTPFile[] fs = ftp.listFiles();
for (FTPFile ff : fs) {
if ("".equals(fileName)){
return flag;
}
if (ff.getName().equals(fileName)){
String filePath = remotePath + "/" +fileName;
ftp.deleteFile(filePath);
flag = true;
}
}
ftp.logout();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException ioe) {
}
}
}
return flag;
}
/**
* @Description 刪除文件夾
* @Author xw
* @Date 11:38 2020/2/6
* @Param [remotePath, fileName]
* @return void
**/
public static boolean deleteFolder( String remotePath){
boolean flag = false;
try {
remotePath = new String(remotePath.getBytes("GBK"),"iso-8859-1");
if (!initFtpClient()){
return flag;
};
// 轉移到FTP服務器目錄
ftp.changeWorkingDirectory(remotePath);
ftp.enterLocalPassiveMode();
FTPFile[] fs = ftp.listFiles();
if (fs.length==0){
ftp.removeDirectory(remotePath);
flag = true;
}
ftp.logout();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException ioe) {
}
}
}
return flag;
}
/**
* @Description 修改文件名稱或者文件夾名
* @Author xw
* @Date 21:18 2020/2/11
* @Param [oldAllName, newAllName]
* @return boolean
**/
public static boolean reNameFile( String oldAllName,String newAllName){
boolean flag = false;
try {
oldAllName = new String(oldAllName.getBytes("GBK"),"iso-8859-1");
newAllName = new String(newAllName.getBytes("GBK"),"iso-8859-1");
if (!initFtpClient()){
return flag;
};
ftp.enterLocalPassiveMode();
ftp.rename(oldAllName,newAllName);
flag = true;
ftp.logout();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ftp.isConnected()) {
try {
ftp.disconnect();
} catch (IOException ioe) {
}
}
}
return flag;
}
/**
* 私有化構造
*/
private FtpUtil(){}
}
cn.rayfoo.web包中創建了BaseController和FileUploadController,原本只想寫文件上傳,一不小心給文件下載和分享也寫了,所以不要糾結類名啦~
BaseController
package cn.rayfoo.web;
import cn.rayfoo.common.Protocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.UUID;
/**
* @Author: rayfoo@qq.com
* @Date: 2020/7/20 4:49 下午
* @Description: controller的公共內容 這里為了簡化FileUploadController的代碼,將非請求方法都放入了baseController中
*/
public class BaseController {
/**
* 日志記錄
*/
Logger logger = LoggerFactory.getLogger(BaseController.class);
/**
* 用於初始化請求、響應、session
*/
protected HttpServletResponse response;
protected HttpServletRequest request;
protected HttpSession session;
//server上傳的基礎路徑
protected String fieldPath = "/upload/";
/**
* 在所有請求執行之前執行 初始化一些參數
* @param request
* @param response
*/
@ModelAttribute
protected void init(HttpServletRequest request,HttpServletResponse response){
this.request = request;
this.response = response;
this.session = request.getSession();
}
/**
* 獲取服務器的url
* @return
*/
protected String getServerURL(){
return Protocol.COMMON_HTTP.getProtocolType() + request.getLocalAddr() + ":" + request.getLocalPort() + request.getContextPath();
}
/**
* 獲取服務器的一個文件夾路徑 會在末尾加上/
* @param dirName 文件夾名稱
* @return
*/
protected String getServerPath(String dirName){
return session.getServletContext().getRealPath( dirName) + "/";
}
/**
* 獲取服務器上的某個文件路徑
* @param dirName 文件夾名稱
* @param fileName 文件名稱
* @return
*/
protected String getServerFile(String dirName,String fileName){
return getServerPath(dirName) + fileName;
}
/**
* 使用uuid替換文件名
* @param file MultipartFile對象
* @return
*/
protected String replaceFileName(MultipartFile file){
//獲取文件名
String fileName = file.getOriginalFilename();
//獲取文件后綴名
String suffix = fileName.substring(fileName.lastIndexOf("."));
//使用uuid替換文件名
fileName = UUID.randomUUID().toString().replace("-", "") + suffix;
//返回文件名
return fileName;
}
}
FileUploadController
package cn.rayfoo.web;
import cn.rayfoo.utils.AliyunOSSUtil;
import cn.rayfoo.utils.FtpUtil;
import cn.rayfoo.utils.QRCodeUtil;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.net.URLEncoder;
import java.util.UUID;
/**
* @Author: rayfoo@qq.com
* @Date: 2020/7/20 4:22 下午
* @Description:
*/
@Controller
public class FileUploadController extends BaseController {
@PostMapping("/serverUpload")
@ResponseBody
public String fileUpload1(@RequestParam("file") MultipartFile file) {
//如果文件為空 返回錯誤信息
if(file.isEmpty()){
return "file not found";
}
//獲取路徑
String uploadPath = getServerPath(fieldPath);
//創建文件對象
File dir = new File(uploadPath);
//判斷文件夾是否存在
if (!dir.exists()) {
//不存在創建文件夾
dir.mkdir();
}
//使用UUID替換文件名
String fileName = replaceFileName(file);
//文件上傳邏輯
try {
//文件上傳
file.transferTo(new File(dir, fileName));
//記錄日志
logger.debug("文件上傳成功:" + uploadPath + fileName);
//文件保存在服務器的URL
String fileURL = getServerURL() + fieldPath + fileName;
//生成二維碼
QRCodeUtil.encode(fileURL, getServerFile("/img/", "logo.jpg"), getServerFile(fieldPath, fileName + ".png"), true);
//返回文件的url,二維碼的url=文件url+.png
return "文件上傳成功:" + fileURL;
} catch (Exception ex) {
ex.printStackTrace();
//記錄日志
logger.debug("文件上傳出現異常:" + ex.getMessage());
//返回錯誤信息
return "文件上傳出現異常:" + ex.getMessage();
}
}
@PostMapping("/ftpUpload")
@ResponseBody
public String ftpUpload(MultipartFile file) {
//如果文件為空 返回錯誤信息
if(file.isEmpty()){
return "file not found";
}
//獲取文件名
String fileName = replaceFileName(file);
//上傳的目錄,如果沒有回會動創建
String filePath = "/img";
try {
//上傳文件
boolean reslut = FtpUtil.uploadFile(filePath, fileName, file.getInputStream());
//判斷是否上傳成功
if (reslut) {
logger.debug("通過ftp文件上傳成功!");
return "success";
}
logger.debug("ftp文件上傳失敗!");
return "error";
} catch (IOException e) {
e.printStackTrace();
logger.debug("ftp上傳服務器發生異常!");
return "error";
}
}
@GetMapping(value = "/ftpDownload/{fileId}")
@ResponseBody
public String ftpDownload(@PathVariable Integer fileId) {
//校驗是否有ftp權限。。。
//ftp目錄,模擬下載 這里的文件路徑和文件名是提前定義的 可以通過id從數據庫中查到
String filePath = "/img";
//獲取完整文件名
String realName = "d795d3b7acd94a0aacba096aca1c2927.jpg";
//去FTP上拉取
try {
OutputStream os = new BufferedOutputStream(response.getOutputStream());
response.setCharacterEncoding("utf-8");
// 設置返回類型
response.setContentType("multipart/form-data");
// 文件名轉碼一下,不然會出現中文亂碼
response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(realName, "UTF-8"));
//下載文件
boolean flag = FtpUtil.downloadFile(filePath, realName, os);
os.flush();
os.close();
if (flag) {
logger.debug("ftp文件下載成功");
return "success";
}
throw new RuntimeException("文件下載失敗");
} catch (Exception e) {
e.printStackTrace();
logger.debug("ftp文件下載失敗:" + e.getMessage());
return "error";
}
}
@GetMapping(value = "/serverDownload/{fileName}/{fileType}")
public ResponseEntity<byte[]> serverDownload(@PathVariable String fileName, @PathVariable String fileType) {
//由於get請求無法正常攜帶.所以需要添加文件類型即后綴
File file = new File(getServerPath(fieldPath) + fileName + "." + fileType);
//創建字節數組,用於返回
byte[] body = null;
//初始化文件流
InputStream is = null;
try {
is = new FileInputStream(file);
//解析文件
body = new byte[is.available()];
//將文件解析為文件流
is.read(body);
} catch (Exception e) {
e.printStackTrace();
logger.debug("server文件下載出現異常:" + e.getMessage());
}
//設置響應頭
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attchement;filename=" + file.getName());
//返回狀態碼、文件
HttpStatus statusCode = HttpStatus.OK;
ResponseEntity<byte[]> entity = new ResponseEntity<>(body, headers, statusCode);
logger.debug("server文件已下載");
return entity;
}
@PostMapping("/aliOSSUpload")@ResponseBody
public String aliOSSUpload(@RequestParam("file")MultipartFile file){
//如果文件為空 返回錯誤信息
if(file.isEmpty()){
return "file not found";
}
//獲取原文件名
String fileName = replaceFileName(file);
//返回圖片的url
String fileURL = AliyunOSSUtil.uploadFileInputSteam(fileName,file);
logger.debug("阿里雲OSS文件上傳成功!");
try {
//生成二維碼 由於阿里雲OSS工具類又在文件之前加了UUID,所以可能會導致文件名和二維碼不一致
QRCodeUtil.encode(fileURL, getServerFile("/img/", "logo.jpg"), getServerFile(fieldPath, fileName + ".png"), true);
logger.debug("阿里雲OSS文件二維碼生成成功!");
//需要將二維碼上傳到服務器,可以使用getServerFile(fieldPath, fileName + ".png"創建File對象,使用AliyunOSSUtil.uploadFileInputSteam(fileName,file);上傳
} catch (Exception e) {
e.printStackTrace();
logger.debug("阿里雲OSS文件上傳失敗:" + e.getMessage());
return "error";
}
//在URL顯示文件的URL,可以直接通過URL下載
return fileURL;
}
}
前端頁面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上傳Demo</title>
<link rel="stylesheet" href="css/bootstrap.min.css">
<script type="application/javascript" src="js/bootstrap.min.js"></script>
<style>
#img{
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<img id="img" src="" alt="" class="img-thumbnail img-responsive">
<h2>服務器文件上傳</h2>
<form action="/serverUpload" method="post" enctype="multipart/form-data">
<input type="file" name="file" onchange="uploadImg(this)">
<input type="submit" id="submit">
</form>
<a href="/serverDownload/3badc9a7099f4a5aa5f5ca58cb9d7e01/jpg">文件下載模擬,真實場景是通過數據庫將數據綁定至a標簽</a>
<hr>
<h2>ftp文件上傳</h2>
<form action="/ftpUpload" method="post" enctype="multipart/form-data">
<input type="file" name="file" onchange="uploadImg(this)">
<input type="submit">
</form>
<hr>
<h2>OSS文件上傳</h2>
<form action="/aliOSSUpload" method="post" enctype="multipart/form-data">
<input type="file" name="file" onchange="uploadImg(this)">
<input type="submit">
</form>
<script>
//選擇圖片,馬上預覽
function uploadImg(obj) {
var file = obj.files[0];
//file.size 單位為byte 可以做上傳大小控制
if(file.size>10485760){
alert("最大支持10M的圖片!");
//大於10M禁止上傳,這里只做了一個處理,其他的方法相同。
document.getElementById("submit").disabled = "disabled";
return;
}
var reader = new FileReader();
//讀取文件過程方法
reader.onloadstart = function (e) {
console.log("開始讀取....");
}
reader.onprogress = function (e) {
console.log("正在讀取中....");
}
reader.onabort = function (e) {
console.log("中斷讀取....");
}
reader.onerror = function (e) {
console.log("讀取異常....");
}
reader.onload = function (e) {
console.log("成功讀取....");
var img = document.getElementById("img");
img.src = e.target.result;
//或者 img.src = this.result; //e.target == this
}
reader.readAsDataURL(file)
}
</script>
</body>
</html>