【问题标题】:Integrating Flash File Upload with JSF将 Flash 文件上传与 JSF 集成
【发布时间】:2012-12-24 23:56:21
【问题描述】:

我看到我们可以通过 Flash 文件上传来上传多个文件。喜欢SWFUploadYUI Uploader。是否可以将这些上传组件与 JSF 集成?

我想要的是一次选择多个文件。 Primefaces 文件上传器具有此功能,但在 IE7 中不起作用,因为 IE7 不支持 HTML5。

我需要创建一个包含各种字段的表单,例如下拉菜单、文本输入等,还需要添加一个文件上传器来选择多个文件。当点击 JSF 提交按钮时,表单将被验证并在之后继续。

我创建了一个用于上传多个文件的页面,但该页面使用多个输入文件组件来处理多个文件。

任何参考资料都会对我很有帮助。我找到了另一个SO thread,并且在那里发布的解决方案使用了JSP。我无法理解如何使用它来满足我的要求。

更新

我创建了以下托管 bean:

import com.mhis.massupload.ucm.Service;

import java.util.List;
import java.util.UUID;
import java.util.logging.Logger;

import javax.faces.context.FacesContext;

import org.apache.commons.fileupload.FileItem;


public class UploadBean { 
    private Logger log = Logger.getLogger(getClass().getName());
    private Service service;
    private String key;

    public UploadBean() {
        super();
        log.info("JYM");
        init();
    }

    private void init() {
        key = UUID.randomUUID().toString();
    }

    public String upload() {        
        System.out.println("Action Invoked.");
        List<FileItem> fileFields = (List<FileItem>)FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get(key);
        System.out.println(fileFields);

        return "";
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getKey() {
        return key;
    }
}

Servlet 是:

import java.io.IOException;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;


public class UploadServlet extends HttpServlet {

    @SuppressWarnings("compatibility:-3560436533054762606")
    private static final long serialVersionUID = 4122845079663279030L;

    public void init(ServletConfig config) throws ServletException {
        super.init(config);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("UploadServlet invoked.");        
        List<FileItem> fileFields = new ArrayList<FileItem>();

        try {
            List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
            for (FileItem item : items) {
                if (!item.isFormField()) {
                    fileFields.add(item);
                    System.out.println(item.getName());
                }
            }
        } catch (Exception e) {
            throw new ServletException(e);
        }

        String key = request.getParameter("key");
        request.getSession().setAttribute(key, fileFields);
    }
}

jspx页面:

<?xml version='1.0' encoding='utf-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns:h="http://java.sun.com/jsf/html">
  <jsp:output omit-xml-declaration="true" doctype-root-element="HTML"
              doctype-system="http://www.w3.org/TR/html4/loose.dtd"
              doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"/>
  <jsp:directive.page contentType="text/html;utf-8"/>
  <f:view>
    <html>
      <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <script src="js/jquery-1.8.3.min.js" type="text/javascript"></script>        
        <script src="uploadify/jquery.uploadify.js" type="text/javascript"></script>
        <link rel="stylesheet" media="screen" href="uploadify/uploadify.css"
              type="text/css"/>
        <script type="text/javascript">
            $(document).ready(function() {
                $('#uploadify').uploadify({
                    'swf': 'uploadify/uploadify.swf',
                    'uploader': '${pageContext.request.contextPath}/uploadServlet;jsessionid=${pageContext.session.id}?key=<h:outputText value="#{uploadBean.key}" />'                   
                });                                         
            });    

            var upload = function() {
                $('#uploadify').uploadify('upload','*');
            }
        </script>
        <title>test</title>
      </head>
      <body>
        <h:form enctype="multipart/form-data">
            <input id="uploadify" type="file"/>            
            <h:commandLink action="#{uploadBean.upload}" value="Upload" />
        </h:form>
      </body>
    </html>
  </f:view>
</jsp:root>

我在这里使用 Uploadify。

我有两个问题:

  • upload 方法的List&lt;FileItem&gt; fileFields 有时会返回null,有时会显示列表。我找不到原因。我试图通过FacesContext.getCurrentInstance().getExternalContext().getSession(false)action方法中获取HttpSession,然后在其上调用getAttribute(),这也总是返回null

  • 如果我设置'auto':false,即调用upload();方法后将启动文件上传并将&lt;h:commandLink/&gt;修改为:&lt;h:commandLink action="#{uploadBean.upload}" value="Upload" onclick="upload();"/&gt;然后我得到异常,即:

    org.apache.commons.fileupload.FileUploadBase$IOFileUploadException: Processing of multipart/form-data request failed. EOF after reading only: '2392369' of: '11626364' promised bytes, out of which at least: '0' were already buffered
    at org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:367)
    at org.apache.commons.fileupload.servlet.ServletFileUpload.parseRequest(ServletFileUpload.java:126)
    at com.edfx.massupload.servlet.UploadServlet.doPost(UploadServlet.java:33)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
    at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
    at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
    at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:300)
    at weblogic.servlet.internal.TailFilter.doFilter(TailFilter.java:26)
    at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
    at oracle.security.jps.ee.http.JpsAbsFilter$1.run(JpsAbsFilter.java:119)
    at java.security.AccessController.doPrivileged(Native Method)
    at oracle.security.jps.util.JpsSubject.doAsPrivileged(JpsSubject.java:315)
    at oracle.security.jps.ee.util.JpsPlatformUtil.runJaasMode(JpsPlatformUtil.java:442)
    at oracle.security.jps.ee.http.JpsAbsFilter.runJaasMode(JpsAbsFilter.java:103)
    at oracle.security.jps.ee.http.JpsAbsFilter.doFilter(JpsAbsFilter.java:171)
    at oracle.security.jps.ee.http.JpsFilter.doFilter(JpsFilter.java:71)
    at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
    at oracle.dms.servlet.DMSServletFilter.doFilter(DMSServletFilter.java:139)
    at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
    at weblogic.servlet.internal.RequestEventsFilter.doFilter(RequestEventsFilter.java:27)
    at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
    at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.wrapRun(WebAppServletContext.java:3715)
    at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3681)
    at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
    at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:120)
    at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2277)
    at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2183)
    at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1454)
    at weblogic.work.ExecuteThread.execute(ExecuteThread.java:209)
    at weblogic.work.ExecuteThread.run(ExecuteThread.java:178)
    

在这种情况下,action 方法也在 servlet 执行之前执行。

如何解决这两个问题?

PS

我需要修改uploadify.js 以设置swf 文件的正确路径并更改取消按钮的css。我已将uploadify 的整个目录放在Web-Content 中。

【问题讨论】:

    标签: java jsf file-upload jsf-1.2 swfupload


    【解决方案1】:

    这是我制作的解决方案,BalusC 帮我开发了很多。

    平台和框架

    • 上传 v3.2
    • JSF 1.2
    • jQuery 1.8.3
    • WebLogic 10.3.5.0
    • Apache Commons 文件上传 1.2.2

    多部分请求的问题

    JSF 1.2 无法处理多部分请求。因此,如果&lt;h:form/&gt; 包含enctype="multipart/form-data" 作为属性,则命令组件的action 方法(如&lt;h:commandButton/&gt;)将不会触发。解决方案是创建一个过滤器,它将与Faces Servlet 一起工作并显式处理多部分请求。这是过滤器:

    package com.mhis.massupload.filter;
    
    import java.io.IOException;
    
    import java.util.Collections;
    import java.util.Enumeration;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    
    import org.apache.commons.fileupload.FileItem;
    import org.apache.commons.fileupload.disk.DiskFileItemFactory;
    import org.apache.commons.fileupload.servlet.ServletFileUpload;
    
    
    public class MultipartFilter implements Filter {
        private FilterConfig filterConfig = null;
    
        public void init(FilterConfig filterConfig) throws ServletException {
            this.filterConfig = filterConfig;
        }
    
        public void destroy() {
            filterConfig = null;
        }
    
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            if (!(request instanceof HttpServletRequest)) {
                chain.doFilter(request, response);
                return;
            }
    
            HttpServletRequest httpRequest = (HttpServletRequest)request;        
            boolean isMultipartContent = ServletFileUpload.isMultipartContent(httpRequest);
    
            if (!isMultipartContent) {
                chain.doFilter(request, response);
                return;
            }
    
            try {
                DiskFileItemFactory factory = new DiskFileItemFactory();
                ServletFileUpload upload = new ServletFileUpload(factory);
                upload.setHeaderEncoding("UTF-8");
                upload.setSizeMax(-1);
    
                List<FileItem> items = upload.parseRequest(httpRequest);
                final Map<String, String[]> parameterMap = new HashMap<String, String[]>();
    
                for (FileItem item : items) {
                    if (item.isFormField()) {
                        processFormField(item, parameterMap);
                    } 
                }
    
                chain.doFilter(new HttpServletRequestWrapper(httpRequest) {
    
                        public Map<String, String[]> getParameterMap() {
                            return parameterMap;
                        }
    
                        public String[] getParameterValues(String name) {
                            return (String[])parameterMap.get(name);
                        }
    
                        public String getParameter(String name) {
                            String[] params = getParameterValues(name);
                            if (params == null) {
                                return null;
                            }
                            return params[0];
                        }
    
                        public Enumeration<String> getParameterNames() {
                            return Collections.enumeration(parameterMap.keySet());
                        }
                    }, response);
            } catch (Exception ex) {
                ServletException servletException = new ServletException();
                servletException.initCause(ex);
                throw servletException;
            }
        }
    
        private void processFormField(FileItem formField, Map<String, String[]> parameterMap) {
            String name = formField.getFieldName();
            String value = formField.getString();
            String[] values = parameterMap.get(name);
    
            if (values == null) {
                parameterMap.put(name, new String[] { value });
            } else {
                int length = values.length;
                String[] newValues = new String[length + 1];
                System.arraycopy(values, 0, newValues, 0, length);
                newValues[length] = value;
                parameterMap.put(name, newValues);
            }
        }
    }
    

    web.xml中这个过滤器的配置是:

    <filter>
        <filter-name>MultipartFilter</filter-name>
        <filter-class>com.mhis.massupload.filter.MultipartFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>MultipartFilter</filter-name>
        <servlet-name>Faces Servlet</servlet-name>
    </filter-mapping>
    

    用于 Uploadify 的 Javasctipt

    $('#uploadify').uploadify({
        'auto': false,
        'buttonText' : 'Browse',
        'fileSizeLimit': 0,
        'swf': 'uploadify/uploadify.swf',
        'uploader': '${pageContext.request.contextPath}/uploadservlet?key=<h:outputText value="#{uploadBean.key}" />',
        'onQueueComplete' : function(queueData) {
            $('.checkIn').click();
        } 
    });
    

    在我的要求中,我需要在单击表单的提交按钮时上传所有文件,而不是在将文件添加到队列中时。这就是我设置'auto': false 的原因。 uploadify 文件夹已放在我的项目的 Web 内容中,因此该插件无法找到 uploadify.swf 文件和取消按钮的图像。我必须修改 jquery.uploadify.js 的第 99 行,我已将其更改为 swf: 'uploadify/uploadify.swf' 以及 uploadify.css 的第 74 行,我已将其更改为:

    .uploadify-queue-item .cancel a {
        background: url('../uploadify/uploadify-cancel.png') 0 0 no-repeat;
        float: right;
        height: 16px;
        text-indent: -9999px;
        width: 16px;
    }
    

    background 设置为 url('../img/uploadify-cancel.png') 0 0 no-repeat;,但我没有 img 文件夹。

    小服务程序

    对于上传文件,我使用了Servlet,即UplaodServlet;这个servlet的配置是:

    <servlet>
        <servlet-name>UploadServlet</servlet-name>
        <servlet-class>com.mhis.massupload.servlet.UploadServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>UploadServlet</servlet-name>
        <url-pattern>/uploadservlet</url-pattern>
    </servlet-mapping>
    

    uploadservlet 已用作uploadify 配置的uploader 属性。我还需要传递一个唯一键作为Servlet 的请求参数。 Servlet 的代码是:

    package com.mhis.massupload.servlet;
    
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.logging.Logger;
    
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.commons.fileupload.FileItem;
    import org.apache.commons.fileupload.disk.DiskFileItemFactory;
    import org.apache.commons.fileupload.servlet.ServletFileUpload;
    
    
    public class UploadServlet extends HttpServlet {
    
        @SuppressWarnings("compatibility:-6472602315203858426")
        private static final long serialVersionUID = -3714619333861571457L;
        private transient Logger log = Logger.getLogger(getClass().getName());
    
        @Override
        public void init(ServletConfig config) throws ServletException {
            super.init(config);
        }
    
        @Override
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException {
            boolean isMultipartContent = ServletFileUpload.isMultipartContent(request);
    
            if (!isMultipartContent) {
                return;
            }
    
            FileItem fileField = null;
            try {
                DiskFileItemFactory factory = new DiskFileItemFactory();
                ServletFileUpload upload = new ServletFileUpload(factory);
                upload.setHeaderEncoding("UTF-8");
                upload.setSizeMax(-1);
    
                List<FileItem> items = upload.parseRequest(request);
                final Map<String, String[]> parameterMap = new HashMap<String, String[]>();
    
    
                for (FileItem item : items) {
                    if (!item.isFormField()) {
                        fileField = item;
                    }
                }
            } catch (Exception ex) {
                log.severe(ex.getMessage());
            }
    
            if (fileField == null) {
                return;
            }
    
            String key = request.getParameter("key");
    
            List<FileItem> fileFields = (List<FileItem>)getServletContext().getAttribute(key);
    
            if (fileFields == null) {
                fileFields = new ArrayList<FileItem>();
                getServletContext().setAttribute(key, fileFields);
            }
    
            fileFields.add(fileField);
        }
    }
    

    我无法使用Session 来放置有关上传文件的信息,因此我使用了ServletContext。更多信息请参考here

    JSF 页面和上传按钮

    由于我需要验证后提交表单时才上传文件,所以我在uploadify的配置中设置了'auto': false。但这给了我一个麻烦,我已经在我原来的问题中发布了这个问题。为了解决这个问题,我声明了三个input[type=button]。其中两个是普通的 HTML 按钮,最后一个是 &lt;h:commandButton/&gt;。我已将此&lt;h:commandButton/&gt; 的可见性设置为false,并使用了一个虚拟按钮来启动文件上传。上传完成后,我以编程方式生成了 click 的 ``. Also I have shown a dummy button which don't have any click event associated with it; this the fail safe if, someone clicks on the Upload button twice during the upload is taking place then the the aforementioned click event fire will work unexpectedly. That why I am showing and hiding the buttons. Here is the completed.jspx` 页面:

    <?xml version='1.0' encoding='utf-8'?>
    <jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
              xmlns:f="http://java.sun.com/jsf/core"
              xmlns:h="http://java.sun.com/jsf/html">
        <jsp:output omit-xml-declaration="true" doctype-root-element="HTML"
        doctype-system="http://www.w3.org/TR/html4/loose.dtd"
        doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"/>
        <jsp:directive.page contentType="text/html;charset=utf-8"/>
        <f:view>
        <html>
            <head>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
            <script src="js/jquery-1.8.3.min.js" type="text/javascript"></script>        
            <script src="uploadify/jquery.uploadify.js" type="text/javascript"></script>
            <link rel="stylesheet" media="screen" href="uploadify/uploadify.css" type="text/css"/>
    
            <script type="text/javascript">
                $(function() {
                $('#uploadify').uploadify({
                    'auto': false,
                    'buttonText' : 'Browse',
                    'fileSizeLimit': 0,
                    'swf': 'uploadify/uploadify.swf',
                    'uploader': '${pageContext.request.contextPath}/uploadservlet?key=<h:outputText value="#{uploadBean.key}" />',
                    'onQueueComplete' : function(queueData) {
                    $('.checkIn').click();
                    } 
                });                                         
    
                $('input[name=actualCheckIn]').on('click', function(event){
                    event.stopPropagation();
                    $(this).hide();
                    $('input[name=fakeCheckIn]').show();
                    $('#uploadify').uploadify('upload','*');                
                    return false;
                });
                });    
    
                var upload = function() {           
                }
            </script>
    
            <title>Upload</title>
            </head>
            <body>
            <h:form enctype="multipart/form-data">
                <input id="uploadify" type="file"/>
                <h:commandButton value="Check In" action="#{uploadBean.upload}" styleClass="checkIn" style="display: none"/>
                <input type="button" value="Check In" name="actualCheckIn"/>
                <input type="button" value="Check In" onclick="return false;" name="fakeCheckIn" style="display: none"/>
            </h:form>
            </body>
        </html>
        </f:view>
    </jsp:root>
    

    这样,当uplaodify 完成时,它会上传到servlet,触发JSF。

    托管 Bean

    托管 bean 的作用域为 Session,下面是它的代码:

    package com.mhis.massupload.bean;
    
    
    import com.mhis.massupload.dto.DocInfo;
    
    import java.io.Serializable;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.UUID;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    import javax.faces.context.FacesContext;
    
    import javax.servlet.ServletContext;
    
    import oracle.stellent.ridc.IdcClientException;
    
    import org.apache.commons.fileupload.FileItem;
    import org.apache.commons.lang3.StringUtils;
    
    
    public class UploadBean implements Serializable {
        @SuppressWarnings("compatibility:-930710930183135088")
        private static final long serialVersionUID = -491462816764274947L;
        private transient Logger log = Logger.getLogger(getClass().getName());
    
        private String key;
        private transient Service service;
    
        public UploadBean() throws IdcClientException {
            init();
        }
    
        private void init() throws IdcClientException {
            key = UUID.randomUUID().toString();        
        }
    
        public String upload() {
            List<FileItem> fileFields = (List<FileItem>) FacesContext.getCurrentInstance().getExternalContext().getApplicationMap().remove(key);
            List<DocInfo> docInfos = new ArrayList<DocInfo>();
    
            if (fileFields != null) {
                for (FileItem fileField : fileFields) {
                    if(StringUtils.isNotBlank(fileField.getName())) {
                        try {
                            System.out.println("Check in: " + fileField.getName());
                        } catch (Exception e) {
                            log.log(Level.SEVERE, e.getMessage());
                        }
                    }                
                }
            }
    
            FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("docInfos", docInfos);
    
            return "report";
        }
    
        public void setKey(String key) {
            this.key = key;
        }
    
        public String getKey() {
            return key;
        }
    }
    

    这就像魅力一样。

    希望对您有所帮助。

    【讨论】:

      【解决方案2】:

      为此,您将需要一个 servlet。我已经回答了一个 Uploadify + Servlet 问题,之前应该提供足够的线索:Uploadify plugin doesn't call Java Servlet。 SWFUpload 和 YUI Uploader 的概念没有什么不同。请注意,您可以毫无问题地将纯 HTML 放入 JSF 页面。

      您的主要问题是使 JSF 支持 bean 和 HTTP servlet 能够相互交换数据。为此,应使用 HTTP 会话。首先让 JSF bean 在初始请求时生成一个唯一键,用作会话属性名称:

      key = UUID.randomUUID().toString();
      

      然后,在您告诉 Uploadify/SWFUpload/etc 使用的上传 URL 中,您需要包含 jsessionid URL 片段(以便 servlet 将使用相同的 HTTP 会话)和 keyas URL 查询字符串参数(以便 servlet 将使用此密钥来存储有关文件上传的相关状态)。假设您需要将其指定为 JS 对象键:

      url: '${pageContext.request.contextPath}/uploadServlet;jsessionid=${pageContext.session.id}?key=<h:outputText value="#{uploadBean.key}" />'
      

      现在,servlet 将在同一个 HTTP 会话中被调用(即 request.getSession() 将与 ExternalContext#getSession() 在 JSF 中可用的完全相同,其属性由 getSessionMap() 提供)。所以,在doPost() 中做:

      String key = request.getParameter("key");
      request.getSession().setAttribute(key, someStateAboutTheUpload);
      

      最后,当 JSF 即将处理表单提交时,只需通过 key 获取所需的数据(在请求范围 bean 的情况下,您可以通过 &lt;input type="hidden"&gt;&lt;t:saveState&gt; 为后续请求存储这些数据):

      SomeState someState = (SomeState) externalContext.getSessionMap().get(key);
      

      唯一键对于功能来说不是必需的,它甚至可以是静态键,但基于每个视图生成的键可确保当最终用户在多个浏览器选项卡中打开相同视图时数据不会发生冲突/windows 在同一个会话中。

      【讨论】:

      • 您好@BalusC,我已尝试按照您的指示实施;虽然我收到两个错误。我已经更新了我的问题。你能告诉我怎么解决吗?
      • 供参考:新问题贴在stackoverflow.com/questions/14275732/…回复
      猜你喜欢
      • 1970-01-01
      • 2012-02-01
      • 2011-02-05
      • 2011-02-18
      • 1970-01-01
      • 2013-09-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多