• Spring MVC-学习笔记(5)spring MVC的文件上传、下载、拦截器


    1、文件上传。

         spring MVC为文件上传提供了直接的支持,这种支持是即插即用的MultipartResolver(多部分解析器)实现的。spring MVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类:CommonsMultipartResolver。因此,spring MVC的文件上传还需要依赖Apache Commons FileUpload的组件。

         spring MVC上下文中默认没有装配MultipartResolver,因此默认情况下不具备文件上传功能。如果需要使用spring的文件上传功能,还需要在上下文配置MultipartResolver。

         Spring MVC的form表单数据类型必须是multipart/form-data类型(二进制流方式),且必须是post方式。使用MultipartFile作为处理方法的参数接收form表单提交的file文件。MultipartFile提供的方法有:

         

       spring MVC上下文默认没有装配MultipartResolver,故要使用上传文件操作,还需要在配置文件中注册MultipartResolver。

    <!-- 文件上传配置 -->
         <bean id="multipartResolver"  
            class="org.springframework.web.multipart.commons.CommonsMultipartResolver">  
            <!-- 上传文件大小上限,单位为字节(10MB) -->
            <property name="maxUploadSize">  
                <value>10485760</value>  
            </property>  
            <!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
            <property name="defaultEncoding">
                <value>UTF-8</value>
            </property>
        </bean>

      此外,还需要Apache Commons FileUpload的组件,将commons-fileupload-1.3.3.jar、commons-io-2.6.jar复制到项目lib下。

      举个例子:

      

       引入的jar包为spring-5.0.5、commons-logging-1.2.jar、commons-io-2.6.jar、commons-fileupload-1.3.3.jar。error.jsp、success.jsp仅是提示页面。

       springmvc-config.xml

    <?xml version="1.0" encoding="utf-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns="http://www.springframework.org/schema/beans"
           xmlns:p="http://www.springframework.org/schema/p"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="
           http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-4.0.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.xsd">
           
          <mvc:default-servlet-handler/>
          
          <!-- 扫描controller -->
          <context:component-scan base-package="com.lfy.controller"/>
          
         <!-- 映射器、适配器策略  -->
          <mvc:annotation-driven/>
          
          <!-- 视图解析器 -->
          <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" 
                p:prefix="/WEB-INF/content/" p:suffix=".jsp" />
                
          <!-- 文件上传配置 -->
          <bean id="multipartResolver"  
                class="org.springframework.web.multipart.commons.CommonsMultipartResolver">  
                <!-- 上传文件大小上限,单位为字节(10MB) -->
                <property name="maxUploadSize">  
                    <value>10485760</value>  
                </property>  
                <!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
                <property name="defaultEncoding">
                    <value>UTF-8</value>
                </property>
          </bean>
    </beans>

       uploadForm.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>文件上传</title>
    </head>
    <body>
        <h2>文件上传</h2>
        <form action="upload" enctype="multipart/form-data" method="post">
            <table>
                <tr>
                    <td>文件描述:</td>
                    <td><input type="text" name="description"></td>
                </tr>
                <tr>
                    <td>请选择文件:</td>
                    <td><input type="file" name="file"></td>
                </tr>
                <tr>
                    <td><input type="submit" value="上传"></td>
                </tr>
            </table>
        </form>
    </body>
    </html>

       FileUploadController.java

    package com.lfy.controller;
    
    import java.io.File;
    import java.net.URLEncoder;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.commons.io.FileUtils;
    import org.springframework.http.MediaType;
    import org.springframework.http.ResponseEntity;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.ModelAttribute;
    import org.springframework.web.bind.annotation.RequestHeader;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.multipart.MultipartFile;
    
    import com.lfy.bean.User;
    
    @Controller
    public class FileUploadController{
        
        @RequestMapping(value="/uploadForm")
        public String uploadForm(){
            // 跳转到文件上传页面
            return "uploadForm";
        }

    // 上传文件会自动绑定到MultipartFile中 @RequestMapping(value="/upload") public String upload(HttpServletRequest request, @RequestParam("description") String description, @RequestParam("file") MultipartFile file) throws Exception{ System.out.println(description); // 如果文件不为空,写入上传路径 if(!file.isEmpty()){ // 上传文件路径 String path = request.getServletContext().getRealPath( "/images"); // 上传文件名 String filename = file.getOriginalFilename(); File filepath = new File(path,filename); // 判断路径是否存在,如果不存在就创建一个 if (!filepath.getParentFile().exists()) { filepath.getParentFile().mkdirs(); } // 将上传文件保存到一个目标文件当中 file.transferTo(new File(path+File.separator+ filename)); System.out.println("上传文件路径:" + (path+File.separator+ filename)); return "success"; }else{ return "error"; } } }

        总结:步骤1.引入commons相关jar包;2.注册MultipartResolver;3.编码接收文件的上传。

        ==>使用对象接收文件的上传:文件作为对象的属性保存

        registerForm.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>用户注册</title>
    </head>
    <body>
        <h2>用户注册</h2>
        <form action="register" enctype="multipart/form-data" method="post">
            <table>
                <tr>
                    <td>用户名:</td>
                    <td><input type="text" name="username"></td>
                </tr>
                <tr>
                    <td>请上传头像:</td>
                    <td><input type="file" name="image"></td>
                </tr>
                <tr>
                    <td><input type="submit" value="注册"></td>
                </tr>
            </table>
        </form>
    </body>
    </html>

       User.java

    package com.lfy.bean;
    
    import java.io.Serializable;
    
    import org.springframework.web.multipart.MultipartFile;
    
    
    public class User implements Serializable{
    
        private static final long serialVersionUID = 1L;
    
        private String username;
        
        private MultipartFile image;
    
        public User() {
            super();
        }
        public String getUsername() {
            return username;
        }
        public void setUsername(String username) {
            this.username = username;
        }
        public MultipartFile getImage() {
            return image;
        }
        public void setImage(MultipartFile image) {
            this.image = image;
        }
    }

        UserImageUpController.java

    package com.lfy.controller;
    
    import java.io.File;
    import java.net.URLEncoder;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.commons.io.FileUtils;
    import org.springframework.http.MediaType;
    import org.springframework.http.ResponseEntity;
    import org.springframework.http.ResponseEntity.BodyBuilder;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.ModelAttribute;
    import org.springframework.web.bind.annotation.RequestHeader;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import com.lfy.bean.User;
    
    @Controller
    public class UserImageUpController{
        
        @RequestMapping(value="/registerForm")
         public String registerForm(){
            // 跳转到文件上传页面
            return "registerForm";
        }
         
        @RequestMapping(value="/register")
         public String register(HttpServletRequest request,
                 @ModelAttribute User user,
                 Model model)throws Exception{
             System.out.println(user.getUsername());
            // 如果文件不为空,写入上传路径
            if(!user.getImage().isEmpty()){
                // 上传文件路径
                String path = request.getServletContext().getRealPath(
                        "/images");
                // 上传文件名
                String filename = user.getImage().getOriginalFilename();
                File filepath = new File(path,filename);
                // 判断路径是否存在,如果不存在就创建一个
                if (!filepath.getParentFile().exists()) { 
                    filepath.getParentFile().mkdirs();
                }
                // 将上传文件保存到一个目标文件当中
                user.getImage().transferTo(new File(path+File.separator+ filename));
                // 将用户添加到model
                model.addAttribute("filename", user.getImage().getOriginalFilename());
                System.out.println("上传文件路径:" + (path+File.separator+ filename));
                return "userInfo";
            }else{
                return "error";
            }
        }
        
         @RequestMapping(value="/download")
         public ResponseEntity<byte[]> download(HttpServletRequest request,
                 @RequestParam("filename") String filename,
                 @RequestHeader("User-Agent") String userAgent
                 )throws Exception{
            // 下载文件路径
            String path = request.getServletContext().getRealPath(
                   "/images");
            // 构建File
            File file = new File(path+File.separator+ filename);
            // ok表示Http协议中的状态 200
           BodyBuilder builder = ResponseEntity.ok();
           // 内容长度
           builder.contentLength(file.length());
           // application/octet-stream : 二进制流数据(最常见的文件下载)。
           builder.contentType(MediaType.APPLICATION_OCTET_STREAM);
           // 使用URLDecoder.decode对文件名进行解码
           filename = URLEncoder.encode(filename, "UTF-8");
           // 设置实际的响应文件名,告诉浏览器文件要用于【下载】、【保存】attachment 以附件形式
           // 不同的浏览器,处理方式不同,要根据浏览器版本进行区别判断
           if (userAgent.indexOf("MSIE") > 0) {
                   // 如果是IE,只需要用UTF-8字符集进行URL编码即可
                   builder.header("Content-Disposition", "attachment; filename=" + filename);
           } else {
                   // 而FireFox、Chrome等浏览器,则需要说明编码的字符集
                   // 注意filename后面有个*号,在UTF-8后面有两个单引号!
                   builder.header("Content-Disposition", "attachment; filename*=UTF-8''" + filename);
           }
           return builder.body(FileUtils.readFileToByteArray(file));
         }
    }

       上面完成一个头像的上传功能,并将文件信息存储在User中,然后将这些信息传递到userInfo.jsp,点击对应的连接,再由download()方法处理下载。

        

    2、文件下载。

       download处理方法接收到页面传递的文件名filename后,使用Apache Commons FileUpload组件的FileUtils读取项目的images文件夹下的该文件,并将其构建成ResponseEntity对象返回客户端下载。使用它可以很方便的定义返回的HttpHeaders和HttpStatus。

       代码中MediaType,代表的是Internet Media Type,即互联网媒体类型,也叫做MIME类型。在Http协议消息中,使用Content-Type来表示具体请求中的媒体类型信息。HttpStatus类型代表的是Http协议中的状态。

    3、拦截器

       Interceptor拦截器,主要作用是拦截用户的请求并进行相应的处理。如用于用户权限验证判断用户是否已经登录

       Spring MVC中的Interceptor拦截器拦截请求是通过实现HandlerInterceptor接口完成的。spring MVC中定义一个Interceptor拦截器非常简单,只需要实现HandlerInterceptor接口,或者继承抽象类HandlerInterceptorAdapter。

       HandlerInterceptor接口定义了三个方法,spring MVC通过这三个方法来对用户的请求进行拦截处理:

        举个例子:强制登录拦截

        

       引入的jar包为spring、commons-logging、javax.servlet.jsp.jstl-1.2.1.jar、javax.servlet.jsp.jstl-api-1.2.1.jar。

       web.xml引入springmvc-config.xml文件。

       springmvc-config.xml

    <?xml version="1.0" encoding="utf-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns="http://www.springframework.org/schema/beans"
           xmlns:p="http://www.springframework.org/schema/p"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="
           http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-4.0.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.xsd">
           
          <mvc:default-servlet-handler/>
          
          <context:component-scan base-package="com.lfy.controller"/>
          
         <!-- 映射器、适配器策略  -->
          <mvc:annotation-driven/>
          
          <!-- 视图解析器 -->
          <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" 
                p:prefix="/WEB-INF/content/" p:suffix=".jsp" />
                
          <!-- 配置拦截器  -->
          <mvc:interceptors>
            <mvc:interceptor>
                <mvc:mapping path="/*"/>
                <!-- 使用bean定义一个Interceptor,直接定义在mvc:interceptors下面的Interceptor将拦截所有的请求 -->  
                 <bean class="com.lfy.Interceptor.AuthorizationInterceptor"/>
            </mvc:interceptor>
          </mvc:interceptors>
    </beans>

       loginForm.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>登录页面</title>
    </head>
    <body>
    <h3>登录页面</h3>
    <form action="login" method="post">
        <!-- 提示信息 -->
        <font color="red">${requestScope.message }</font>
         <table>
             <tr>
                 <td><label>登录名: </label></td>
                 <td><input type="text" id="loginname" name="loginname" ></td>
             </tr>
             <tr>
                 <td><label>密码: </label></td>
                 <td><input type="password" id="password" name="password" ></td>
             </tr>
             <tr>
                 <td><input type="submit" value="登录"></td>
             </tr>
         </table>
    </form>
    </body>
    </html>

       mian.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>首页</title>
    <style type="text/css">
        table{border-collapse:collapse;border-spacing:0;border-left:1px solid #888;border-top:1px solid #888;background:#efefef;}
        th,td{border-right:1px solid #888;border-bottom:1px solid #888;padding:5px 15px;}
        th{font-weight:bold;background:#ccc;}
    </style>
    </head>
    <body>
    <h3>欢迎[${sessionScope.user.username }]访问</h3>
    <br>
    <table border="1">
        <tr>
            <th>封面</th><th>书名</th><th>作者</th><th>价格</th>
        </tr>
        <c:forEach items="${requestScope.book_list }" var="book">
            <tr>
                <td><img src="images/${book.image }" height="60"></td>
                <td>${book.name }</td>
                <td>${book.author }</td>
                <td>${book.price }</td>
            </tr>
        </c:forEach>
    </table>
    </body>
    </html>

       AuthorizationInterceptor.java

    package com.lfy.Interceptor;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import com.lfy.bean.User;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    /** 
     * 拦截器必须实现HandlerInterceptor接口
     * */ 
    public class AuthorizationInterceptor  implements HandlerInterceptor {
    
        // 不拦截"/loginForm"和"/login"请求
        private static final String[] IGNORE_URI = {"/loginForm", "/login"};
        
         /** 
         * 该方法将在整个请求完成之后执行, 主要作用是用于清理资源的,
         * 该方法也只能在当前Interceptor的preHandle方法的返回值为true时才会执行。 
         */  
        @Override
        public void afterCompletion(HttpServletRequest request,
                HttpServletResponse response, Object handler, Exception exception)
                throws Exception {
            System.out.println("AuthorizationInterceptor afterCompletion --> ");
            
        }
        /** 
         * 该方法将在Controller的方法调用之后执行, 方法中可以对ModelAndView进行操作 ,
         * 该方法也只能在当前Interceptor的preHandle方法的返回值为true时才会执行。 
         */
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response,
                Object handler, ModelAndView mv) throws Exception {
            System.out.println("AuthorizationInterceptor postHandle --> ");
            
        }
    
         /** 
         * preHandle方法是进行处理器拦截用的,该方法将在Controller处理之前进行调用,
         * 该方法的返回值为true拦截器才会继续往下执行,该方法的返回值为false的时候整个请求就结束了。 
         */  
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                Object handler) throws Exception {
            System.out.println("AuthorizationInterceptor preHandle --> ");
            // flag变量用于判断用户是否登录,默认为false 
            boolean flag = false; 
            //获取请求的路径进行判断
            String servletPath = request.getServletPath();
            // 判断请求是否需要拦截
            for (String s : IGNORE_URI) {
                if (servletPath.contains(s)) {
                    flag = true;
                    break;
                }
            }
            // 拦截请求
            if (!flag){
                // 1.获取session中的用户 
                User user = (User) request.getSession().getAttribute("user");
                // 2.判断用户是否已经登录 
                if(user == null){
                    // 如果用户没有登录,则设置提示信息,跳转到登录页面
                     System.out.println("AuthorizationInterceptor拦截请求:");
                     request.setAttribute("message", "请先登录再访问网站");
                     request.getRequestDispatcher("loginForm").forward(request, response);
                }else{
                    // 如果用户已经登录,则验证通过,放行
                     System.out.println("AuthorizationInterceptor放行请求:");
                     flag = true;
                }
            }
            return flag;
        }
    }

       User.java

    package com.lfy.bean;
    
    import java.io.Serializable;
    
    public class User implements Serializable{
    
        private static final long serialVersionUID = 1L;
        
        private Integer id;            // id
        private String loginname;    // 登录名
        private String password;    // 密码
        private String username;    // 用户名
        
        public User() {
            super();
        }
        public Integer getId() {
            return id;
        }
        public void setId(Integer id) {
            this.id = id;
        }
        public String getLoginname() {
            return loginname;
        }
        public void setLoginname(String loginname) {
            this.loginname = loginname;
        }
        public String getPassword() {
            return password;
        }
        public void setPassword(String password) {
            this.password = password;
        }
        public String getUsername() {
            return username;
        }
        public void setUsername(String username) {
            this.username = username;
        }
        
        @Override
        public String toString() {
            return "User [id=" + id + ", loginname=" + loginname + ", password="
                    + password + ", username=" + username + "]";
        }
    }

       Book.java

    package com.lfy.bean;
    
    import java.io.Serializable;
    
    public class Book implements Serializable{
        
        private static final long serialVersionUID = 1L;
        
        private Integer id;                // id    
        private String name;            // 书名
        private String author;            // 作者
        private Double price;            // 价格
        private String image;            // 封面图片
        
        public Book() {
            super();
        }
        public Book( String image,String name, String author, Double price) {
            super();
            this.image = image;
            this.name = name;
            this.author = author;
            this.price = price;
        }
        public Integer getId() {
            return id;
        }
        public void setId(Integer id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getAuthor() {
            return author;
        }
        public void setAuthor(String author) {
            this.author = author;
        }
        
        public Double getPrice() {
            return price;
        }
        public void setPrice(Double price) {
            this.price = price;
        }
        public String getImage() {
            return image;
        }
        public void setImage(String image) {
            this.image = image;
        }
        @Override
        public String toString() {
            return "Book [id=" + id + ", name=" + name + ", author=" + author
                    + ", price=" + price + ", image=" + image + "]";
        }
    }

       FormController.java

    package com.lfy.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    
    /**
     * 动态页面跳转控制器
     * */
    @Controller
    public class FormController{
    
        @GetMapping(value="/loginForm")
         public String loginForm(){
            // 跳转到登录页面
            return "loginForm";
        }
    }

       UserController.java

    package com.lfy.controller;
    
    import javax.servlet.http.HttpSession;
    
    import com.lfy.bean.User;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.servlet.ModelAndView;
    
    /**
     * 处理用户请求控制器
     * */
    @Controller
    public class UserController {
    
         /**
         * 处理/login请求
         * */
         @PostMapping(value="/login")
         public ModelAndView login(
                 String loginname,String password,
                 ModelAndView mv,
                 HttpSession session){
            // 模拟数据库根据登录名和密码查找用户,判断用户登录
            if(loginname != null && loginname.equals("lfy")
                    && password!= null && password.equals("123456")){
                // 模拟创建用户
                User user = new User();
                user.setLoginname(loginname);
                user.setPassword(password);
                user.setUsername("管理员");
                // 登录成功,将user对象设置到HttpSession作用范围域
                session.setAttribute("user", user);
                // 转发到main请求
                mv.setViewName("redirect:main");
            }else{
                // 登录失败,设置失败提示信息,并跳转到登录页面
                mv.addObject("message", "登录名或密码错误,请重新输入!");
                mv.setViewName("loginForm");
            }
            return mv;
        }
    }

       BookController.java

    package com.lfy.controller;
    
    import java.util.ArrayList;
    import java.util.List;
    import com.lfy.bean.Book;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /**
     * 处理图书请求控制器
     * */
    @Controller
    public class BookController {
    /** * 处理/main请求 * */ @RequestMapping(value="/main") public String main(Model model){ // 模拟数据库获得所有图书集合 List<Book> book_list = new ArrayList<Book>(); book_list.add(new Book("java.jpg","疯狂Java讲义(附光盘)","李刚 编著",74.2)); book_list.add(new Book("ee.jpg","轻量级Java EE企业应用实战","李刚 编著",59.2)); book_list.add(new Book("sql0.png","mysql技术内幕-sql编程","姜成尧 编著",60.6)); book_list.add(new Book("sql1.png","mysql调优","贺春旸 编著",66.6)); // 将图书集合添加到model当中 model.addAttribute("book_list", book_list); // 跳转到main页面 return "main"; } }

      运行结果:

      

  • 相关阅读:
    配置变量的信息
    Smarty保留变量信息
    选择排序
    java.utils.HashMap数据结构分析
    HashMap的工作原理
    Dubbo
    五种单例模式:
    Redis的持久化机制包括RBD和AOF两种,对于这两种持久化方式各有优势
    Zookeeper要安装在奇数个节点,但是为什么?
    Redis搭建多台哨兵
  • 原文地址:https://www.cnblogs.com/ZeroMZ/p/11411848.html
Copyright © 2020-2023  润新知