• SpringMVC文件上传的三种方法


    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{
                 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>
    

    源码:gtiee地址github地址

  • 相关阅读:
    Linxu 挂载光盘和硬盘
    Linux firewall
    指纹获取 Fingerprint2
    Vue 封装的组件生命周期钩子
    vue富文本编辑,编辑自动预览,单个图片上传不能预览的问题解决:
    vue 集成百度富文本编辑器
    axios 的二次封装
    element 列表中已选的标记
    element 表单的input循环生成,并可单个input失去焦点单个验证并保存; (多个表单实例)
    axios 二进制流导出
  • 原文地址:https://www.cnblogs.com/zhangruifeng/p/13355881.html
Copyright © 2020-2023  润新知