【问题标题】:Recursively list files in Java递归列出Java中的文件
【发布时间】:2011-01-04 14:25:35
【问题描述】:

如何递归列出Java目录下的所有文件?该框架是否提供任何实用程序?

我看到了很多 hacky 实现。但没有来自框架或nio

【问题讨论】:

  • 我刚刚完成了Test Results,它为许多答案提供了性能测试。不出所料,所有基于 NIO 的答案都表现最佳。 commons-io 的答案显然是表现最差的,运行时间超过两倍。
  • Java8 : Files.walk ?

标签: java file recursion java-7 nio


【解决方案1】:

Java 8 提供了一个很好的流来处理树中的所有文件。

Files.walk(Paths.get(path))
        .filter(Files::isRegularFile)
        .forEach(System.out::println);

这提供了一种自然的方式来遍历文件。由于它是一个流,因此您可以对结果进行所有不错的流操作,例如限制、分组、映射、提前退出等。

更新:我可能会指出还有Files.find,它采用BiPredicate,如果您需要检查文件属性可能会更有效。

Files.find(Paths.get(path),
           Integer.MAX_VALUE,
           (filePath, fileAttr) -> fileAttr.isRegularFile())
        .forEach(System.out::println);

请注意,虽然 JavaDoc 没有提到此方法可能比 Files.walk 更有效,但实际上是相同的,但如果您还在过滤器中检索文件属性,则可以观察到性能差异。最后,如果需要对属性进行过滤使用Files.find,否则使用Files.walk,主要是因为有重载,更方便。

测试:根据要求,我提供了许多答案的性能比较。查看Github project which contains results and a test case

【讨论】:

  • 即使是初学者也能展示函数式编程魔力的例子之一。
  • 与 pre-java 8 方法相比,它的性能如何?我当前的目录遍历太慢了,我正在寻找可以加快它的东西。
  • @BrettRyan,我尝试了您的解决方案,但出现异常 Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException。我该如何纠正它
  • 如何从中获取文件的实际列表?
  • Files.walk 看起来不错,但很多时候是没用的......如果你甚至无法访问一个文件,它会抛出异常......而你什么都没有......跨度>
【解决方案2】:

FileUtilsiterateFileslistFiles 方法。试一试。 (来自commons-io

编辑:您可以check here 获取不同方法的基准。看来 commons-io 的方法很慢,所以选择一些更快的方法from here(如果重要的话)

【讨论】:

  • FYI/TLDR:如果您只想递归地列出所有文件而不进行过滤,请执行FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE),其中dir 是指向基目录的文件对象。
  • 您可能需要考虑使用listFilesAndDirs(),因为listFiles() 不会返回空文件夹。
  • @MikeFHay 查看 FileUtils 代码,我认为应该是 FileUtils.listFiles(dir, true, true)。使用FileUtils.listFiles(dir, null, true) 会抛出异常,而FileUtils.listFiles(dir, true, null) 会列出所有文件而不查看子目录。
  • JDK 原生库怎么样?我可以很容易地实现这个,但我只是来自其他地方的 C&P
  • 我将一些测试放在一起,但到目前为止,这似乎比使用 JDK8 或 JDK7 替代品慢 4 倍。符号链接也被证明是这种方法的问题,特别是当它们链接到树中更高的目录时,这会导致该方法永远不会返回,这可以通过处理过滤器来避免,但不幸的是,即使作为一个文件。
【解决方案3】:

//准备运行

import java.io.File;

public class Filewalker {

    public void walk( String path ) {

        File root = new File( path );
        File[] list = root.listFiles();

        if (list == null) return;

        for ( File f : list ) {
            if ( f.isDirectory() ) {
                walk( f.getAbsolutePath() );
                System.out.println( "Dir:" + f.getAbsoluteFile() );
            }
            else {
                System.out.println( "File:" + f.getAbsoluteFile() );
            }
        }
    }

    public static void main(String[] args) {
        Filewalker fw = new Filewalker();
        fw.walk("c:\\" );
    }

}

【讨论】:

  • 请注意,对于指向路径层次结构中更高路径的符号链接,将导致该方法永远不会结束。考虑一个带有指向-> . 的符号链接的路径。
  • 这实际上是 Files.walkFileTree 的错误实现。我建议人们查看 FIles.walkFileTree 而不是自己尝试滚动它......它可以处理@BrettRyan 指出的确切问题。
  • 感谢您包含 import java.io.File;。如此多的示例忘记包含名称空间的内容甚至数据类型的内容,使示例成为探索之旅的起点。在这里,此示例已准备好运行。谢谢。
  • 路径可能因 Filewalker 文件的位置而异。根目录、当前工作目录、父目录分别使用"/""./""../"
【解决方案4】:

Java 7 会有Files.walkFileTree:

如果您提供一个起点和一个文件访问者,它将在文件访问者遍历文件树中的文件时调用各种方法。我们希望人们在开发递归副本、递归移动、递归删除或设置权限或对每个文件执行其他操作的递归操作时使用此功能。

现在有一个完整的Oracle tutorial on this question

【讨论】:

  • 而且它从不通知步行结束。
【解决方案5】:

不需要外部库。
返回一个集合,这样你就可以在调用后对它做任何你想做的事情。

public static Collection<File> listFileTree(File dir) {
    Set<File> fileTree = new HashSet<File>();
    if(dir==null||dir.listFiles()==null){
        return fileTree;
    }
    for (File entry : dir.listFiles()) {
        if (entry.isFile()) fileTree.add(entry);
        else fileTree.addAll(listFileTree(entry));
    }
    return fileTree;
}

【讨论】:

    【解决方案6】:

    我会选择类似的东西:

    public void list(File file) {
        System.out.println(file.getName());
        File[] children = file.listFiles();
        for (File child : children) {
            list(child);
        }
    }
    

    System.out.println 只是用来指示对文件执行某些操作。无需区分文件和目录,因为普通文件只会有零个子文件。

    【讨论】:

    • 来自listFiles()的文档:“如果这个抽象路径名不表示目录,那么这个方法返回null。”
    • 改进的变体 public static Collection listFileTree(File dir) { if (null == dir || !dir.isDirectory()) { return Collections.emptyList(); } final Set fileTree = new HashSet(); for (文件条目:dir.listFiles()) { if (entry.isFile()) { fileTree.add(entry); } else { fileTree.addAll(listFileTree(entry)); } } 返回文件树; }
    • 对我来说,这是递归的最简洁的答案。
    【解决方案7】:

    对于这种简单的遍历,我更喜欢使用队列而不是递归:

    List<File> allFiles = new ArrayList<File>();
    Queue<File> dirs = new LinkedList<File>();
    dirs.add(new File("/start/dir/"));
    while (!dirs.isEmpty()) {
      for (File f : dirs.poll().listFiles()) {
        if (f.isDirectory()) {
          dirs.add(f);
        } else if (f.isFile()) {
          allFiles.add(f);
        }
      }
    }
    

    【讨论】:

    • 但是您的算法无法打印缩进输出。目录和文件乱七八糟。有什么解决办法吗?
    【解决方案8】:

    只需使用简单的递归自己编写即可:

    public List<File> addFiles(List<File> files, File dir)
    {
        if (files == null)
            files = new LinkedList<File>();
    
        if (!dir.isDirectory())
        {
            files.add(dir);
            return files;
        }
    
        for (File file : dir.listFiles())
            addFiles(files, file);
        return files;
    }
    

    【讨论】:

    • 拜托!让调用者初始化文件列表,这样它就不必每次都检查它的无效性。如果要创建第二个(公共)方法来创建列表,请调用此内部方法并返回完整列表。
    • 随便。空检查不是很贵,除了方便+个人喜好,我认为他会明白的。
    • 你能详细解释一下吗?
    【解决方案9】:

    在 Java 7 中,您可以使用以下类:

    import java.io.IOException;
    import java.nio.file.FileVisitResult;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.nio.file.SimpleFileVisitor;
    import java.nio.file.attribute.BasicFileAttributes;
    
    public class MyFileIterator extends SimpleFileVisitor<Path>
    {
        public MyFileIterator(String path) throws Exception
        {
            Files.walkFileTree(Paths.get(path), this);
        }
    
        @Override
        public FileVisitResult visitFile(Path file,
                BasicFileAttributes attributes) throws IOException
        {
            System.out.println("File: " + file);
            return FileVisitResult.CONTINUE;
        }
    
        @Override
        public FileVisitResult preVisitDirectory(Path dir,
                BasicFileAttributes attributes) throws IOException
        {
            System.out.println("Dir: " + dir);
            return FileVisitResult.CONTINUE;
        }
    }
    

    【讨论】:

      【解决方案10】:

      此代码已准备好运行

      public static void main(String... args) {
          File[] files = new File("D:/").listFiles();
          if (files != null) 
             getFiles(files);
      }
      
      public static void getFiles(File[] files) {
          for (File file : files) {
              if (file.isDirectory()) {
                  getFiles(file.listFiles());
              } else {
                  System.out.println("File: " + file);
              }
          }
      }
      

      【讨论】:

        【解决方案11】:

        我认为这应该可以完成工作:

        File dir = new File(dirname);
        String[] files = dir.list();
        

        这样你就有了文件和目录。现在使用递归并对 dirs 执行相同的操作(File 类具有 isDirectory() 方法)。

        【讨论】:

        • “使用递归”没有帮助。应该有一个例子。
        【解决方案12】:

        在 Java 8 中,我们现在可以使用 Files 实用程序来遍历文件树。很简单。

        Files.walk(root.toPath())
              .filter(path -> !Files.isDirectory(path))
              .forEach(path -> System.out.println(path));
        

        【讨论】:

          【解决方案13】:

          除了递归遍历之外,还可以使用基于访问者的方法。

          下面的代码是使用基于Visitor的方式进行遍历。预计程序的输入是要遍历的根目录。

          public interface Visitor {
              void visit(DirElement d);
              void visit(FileElement f);
          }
          
          public abstract class Element {
              protected File rootPath;
              abstract void accept(Visitor v);
          
              @Override
              public String toString() {
                  return rootPath.getAbsolutePath();
              }
          }
          
          public class FileElement extends Element {
              FileElement(final String path) {
                  rootPath = new File(path);
              }
          
              @Override
              void accept(final Visitor v) {
                  v.visit(this);
              }
          }
          
          public class DirElement extends Element implements Iterable<Element> {
              private final List<Element> elemList;
              DirElement(final String path) {
                  elemList = new ArrayList<Element>();
                  rootPath = new File(path);
                  for (File f : rootPath.listFiles()) {
                      if (f.isDirectory()) {
                          elemList.add(new DirElement(f.getAbsolutePath()));
                      } else if (f.isFile()) {
                          elemList.add(new FileElement(f.getAbsolutePath()));
                      }
                  }
              }
          
              @Override
              void accept(final Visitor v) {
                  v.visit(this);
              }
          
              public Iterator<Element> iterator() {
                  return elemList.iterator();
              }
          }
          
          public class ElementWalker {
              private final String rootDir;
              ElementWalker(final String dir) {
                  rootDir = dir;
              }
          
              private void traverse() {
                  Element d = new DirElement(rootDir);
                  d.accept(new Walker());
              }
          
              public static void main(final String[] args) {
                  ElementWalker t = new ElementWalker("C:\\temp");
                  t.traverse();
              }
          
              private class Walker implements Visitor {
                  public void visit(final DirElement d) {
                      System.out.println(d);
                      for(Element e:d) {
                          e.accept(this);
                      }
                  }
          
                  public void visit(final FileElement f) {
                      System.out.println(f);
                  }
              }
          }
          

          【讨论】:

            【解决方案14】:

            您可以使用以下代码递归获取特定文件夹或目录的文件列表。

            public static void main(String args[]) {
            
                    recusiveList("D:");
            
                }
            
                public static void recursiveList(String path) {
            
                    File f = new File(path);
                    File[] fl = f.listFiles();
                    for (int i = 0; i < fl.length; i++) {
                        if (fl[i].isDirectory() && !fl[i].isHidden()) {
                            System.out.println(fl[i].getAbsolutePath());
                            recusiveList(fl[i].getAbsolutePath());
                        } else {
                            System.out.println(fl[i].getName());
                        }
                    }
                }
            

            【讨论】:

              【解决方案15】:

              我想出这个来递归打印所有文件/文件名。

              private static void printAllFiles(String filePath,File folder) {
                  if(filePath==null) {
                      return;
                  }
                  File[] files = folder.listFiles();
                  for(File element : files) {
                      if(element.isDirectory()) {
                          printAllFiles(filePath,element);
                      } else {
                          System.out.println(" FileName "+ element.getName());
                      }
                  }
              }
              

              【讨论】:

                【解决方案16】:

                列出所有具有提供扩展名的文件,并可选择扫描 子文件夹(递归)

                 public static ArrayList<File> listFileTree(File dir,boolean recursive) {
                        if (null == dir || !dir.isDirectory()) {
                            return new ArrayList<>();
                        }
                        final Set<File> fileTree = new HashSet<File>();
                        FileFilter fileFilter = new FileFilter() {
                            private final String[] acceptedExtensions = new String[]{"jpg", "png", "webp", "jpeg"};
                
                            @Override
                            public boolean accept(File file) {
                                if (file.isDirectory()) {
                                    return true;
                                }
                                for (String extension : acceptedExtensions) {
                                    if (file.getName().toLowerCase().endsWith(extension)) {
                                        return true;
                                    }
                                }
                                return false;
                            }
                        };
                        File[] listed = dir.listFiles(fileFilter);
                        if(listed!=null){
                            for (File entry : listed) {
                                if (entry.isFile()) {
                                    fileTree.add(entry);
                                } else if(recursive){
                                    fileTree.addAll(listFileTree(entry,true));
                                }
                            }
                        }
                        return new ArrayList<>(fileTree);
                    }
                

                【讨论】:

                  【解决方案17】:

                  具有单个列表的非递归 BFS(特定示例是搜索 *.eml 文件):

                      final FileFilter filter = new FileFilter() {
                          @Override
                          public boolean accept(File file) {
                              return file.isDirectory() || file.getName().endsWith(".eml");
                          }
                      };
                  
                      // BFS recursive search
                      List<File> queue = new LinkedList<File>();
                      queue.addAll(Arrays.asList(dir.listFiles(filter)));
                  
                      for (ListIterator<File> itr = queue.listIterator(); itr.hasNext();) {
                          File file = itr.next();
                          if (file.isDirectory()) {
                              itr.remove();
                              for (File f: file.listFiles(filter)) itr.add(f);
                          }
                      }
                  

                  【讨论】:

                    【解决方案18】:

                    我的版本(当然我可以使用 Java 8 中的内置 walk ;-)):

                    public static List<File> findFilesIn(File rootDir, Predicate<File> predicate) {
                            ArrayList<File> collected = new ArrayList<>();
                            walk(rootDir, predicate, collected);
                            return collected;
                        }
                    
                        private static void walk(File dir, Predicate<File> filterFunction, List<File> collected) {
                            Stream.of(listOnlyWhenDirectory(dir))
                                    .forEach(file -> walk(file, filterFunction, addAndReturn(collected, file, filterFunction)));
                        }
                    
                        private static File[] listOnlyWhenDirectory(File dir) {
                            return dir.isDirectory() ? dir.listFiles() : new File[]{};
                        }
                    
                        private static List<File> addAndReturn(List<File> files, File toAdd, Predicate<File> filterFunction) {
                            if (filterFunction.test(toAdd)) {
                                files.add(toAdd);
                            }
                            return files;
                        }
                    

                    【讨论】:

                      【解决方案19】:

                      这是一个使用recursion 的简单但完美的解决方案:

                      public static List<Path> listFiles(String rootDirectory)
                      {
                          List<Path> files = new ArrayList<>();
                          listFiles(rootDirectory, files);
                      
                          return files;
                      }
                      
                      private static void listFiles(String path, List<Path> collectedFiles)
                      {
                          File root = new File(path);
                          File[] files = root.listFiles();
                      
                          if (files == null)
                          {
                              return;
                          }
                      
                          for (File file : files)
                          {
                              if (file.isDirectory())
                              {
                                  listFiles(file.getAbsolutePath(), collectedFiles);
                              } else
                              {
                                  collectedFiles.add(file.toPath());
                              }
                          }
                      }
                      

                      【讨论】:

                        【解决方案20】:
                            private void fillFilesRecursively(File file, List<File> resultFiles) {
                                if (file.isFile()) {
                                    resultFiles.add(file);
                                } else {
                                    for (File child : file.listFiles()) {
                                        fillFilesRecursively(child, resultFiles);
                                    }
                                }
                            }
                        

                        【讨论】:

                          【解决方案21】:

                          Kotlin 有 FileTreeWalk 用于此目的。例如:

                          dataDir.walkTopDown().filter { !it.isDirectory }.joinToString("\n") {
                             "${it.toRelativeString(dataDir)}: ${it.length()}"
                          }
                          

                          将生成给定根目录下所有非目录文件的文本列表,每行一个文件,包含相对于根目录的路径和长度。

                          【讨论】:

                            【解决方案22】:

                            accepted answer 很棒,但是当你想在 lambda 中执行 IO 时它会崩溃。

                            如果您的操作声明 IOExceptions,您可以执行以下操作。

                            您可以将过滤后的流视为Iterable,然后在常规的 for-each 循环中执行您的操作。这样,您就不必在 lambda 中处理异常。

                            try (Stream<Path> pathStream = Files.walk(Paths.get(path))
                                    .filter(Files::isRegularFile)) {
                            
                                for (Path file : (Iterable<Path>) pathStream::iterator) {
                                    // something that throws IOException
                                    Files.copy(file, System.out);
                                }
                            }
                            

                            在这里找到这个技巧:https://stackoverflow.com/a/32668807/1207791

                            【讨论】:

                              【解决方案23】:

                              即使有人已经提供了 Java 8 walk,您也可以这样做。

                              这个会递归地为你提供所有文件

                                private Stream<File> files(File file) {
                                  return file.isDirectory()
                                          ? Arrays.stream(file.listFiles()).flatMap(this::files)
                                          : Stream.of(file);
                              }
                              

                              【讨论】:

                                【解决方案24】:
                                public static String getExten(String path) {
                                    int i = path.lastIndexOf('.');
                                    if (i > 0) {
                                       return path.substring(i);
                                    }
                                    else return "";
                                }
                                public static List<String> GetAllFiles(String path, List<String>fileList){
                                    File file = new File(path);
                                    
                                    File[] files = file.listFiles();
                                    for(File folder:files) {
                                        if(extensions.contains(getExten(folder.getPath()))) {
                                            fileList.add(folder.getPath());
                                        }
                                    }
                                    File[] direcs = file.listFiles(File::isDirectory);
                                    for(File dir:direcs) {
                                        GetAllFiles(dir.getPath(),fileList);
                                    }
                                    return fileList;
                                    
                                }
                                

                                这是一个简单的递归函数,应该为您提供所有文件。 extensions 是一个字符串列表,仅包含那些被接受的扩展名。示例扩展 = [".txt",".docx"] 等。

                                【讨论】:

                                  【解决方案25】:

                                  使用 java.nio 中的 Files.find() 在目录递归搜索子目录中输出 *.csv 文件的示例:

                                  String path = "C:/Daten/ibiss/ferret/";
                                      logger.debug("Path:" + path);
                                      try (Stream<Path> fileList = Files.find(Paths.get(path), Integer.MAX_VALUE,
                                              (filePath, fileAttr) -> fileAttr.isRegularFile() && filePath.toString().endsWith("csv"))) {
                                          List<String> someThingNew = fileList.sorted().map(String::valueOf).collect(Collectors.toList());
                                          for (String t : someThingNew) {
                                              t.toString();
                                              logger.debug("Filename:" + t);
                                          }
                                  
                                      }
                                  

                                  发布此示例,因为我无法理解如何在 Bryan 给出的 #1 示例中传递文件名参数,在 Stream-result 上使用 foreach -

                                  希望这会有所帮助。

                                  【讨论】:

                                    【解决方案26】:

                                    基于@Michael 的回答,添加检查 listFiles 是否返回 null

                                    static Stream<File> files(File file) {
                                        return file.isDirectory()
                                                ? Optional.ofNullable(file.listFiles()).map(Stream::of).orElseGet(Stream::empty).flatMap(MainActivity::files)
                                                : Stream.of(file);
                                    }
                                    

                                    或使用Lightweight-Stream-API,支持Android5 & Android6

                                    static Stream<File> files(File f) {
                                        return f.isDirectory() ? Stream.ofNullable(f.listFiles()).flatMap(MainActivity::files) : Stream.of(f);
                                    }
                                    

                                    【讨论】:

                                      【解决方案27】:

                                      接受的答案很差,因为它可能导致资源泄漏。

                                      Files.walkDirectoryStreams 支持。

                                      返回的流封装了一个或多个 DirectoryStream。如果需要及时处理文件系统资源,则应使用 try-with-resources 构造来确保在流操作完成后调用流的 close 方法。对关闭的流进行操作将导致 IllegalStateException。

                                      必须按照其 javadoc 中的规定关闭 DirectoryStream:

                                      DirectoryStream 在创建时打开,并通过调用 close 方法关闭。关闭目录流会释放与该流关联的所有资源。未能关闭流可能会导致资源泄漏。 try-with-resources 语句提供了一个有用的结构来确保流被关闭:

                                      Path dir = ...
                                      try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
                                          for (Path entry: stream) {
                                              ...
                                          }
                                      }
                                      

                                      因此,真正的答案是:

                                      try (Stream<Path> stream = Files.walk(Paths.get(path))) {
                                          // Do something with the stream.
                                          stream.filter(Files::isRegularFile)
                                                .forEach(System.out::println);
                                      }
                                      

                                      【讨论】:

                                        【解决方案28】:

                                        List filePaths = Files.find(Paths.get(dir),Integer.MAX_VALUE,(filePath, fileAttr) -> fileAttr.isRegularFile() || fileAttr.isDirectory()).collect(Collectors.toList()) ;

                                        filePaths -> 将有文件和文件夹列表,可以迭代并进一步进行

                                        【讨论】:

                                          【解决方案29】:

                                          基于堆垛机的回答。这是一个在 JSP 中工作的解决方案,没有任何外部库,因此您几乎可以将它放在服务器上的任何位置:

                                          <!DOCTYPE html>
                                          <%@ page session="false" %>
                                          <%@ page import="java.util.*" %>
                                          <%@ page import="java.io.*" %>
                                          <%@ page contentType="text/html; charset=UTF-8" %>
                                          
                                          <%!
                                              public List<String> files = new ArrayList<String>();
                                              /**
                                                  Fills files array with all sub-files.
                                              */
                                              public void walk( File root ) {
                                                  File[] list = root.listFiles();
                                          
                                                  if (list == null) return;
                                          
                                                  for ( File f : list ) {
                                                      if ( f.isDirectory() ) {
                                                          walk( f );
                                                      }
                                                      else {
                                                          files.add(f.getAbsolutePath());
                                                      }
                                                  }
                                              }
                                          %>
                                          <%
                                              files.clear();
                                              File jsp = new File(request.getRealPath(request.getServletPath()));
                                              File dir = jsp.getParentFile();
                                              walk(dir);
                                              String prefixPath = dir.getAbsolutePath() + "/";
                                          %>
                                          

                                          然后你就做这样的事情:

                                              <ul>
                                                  <% for (String file : files) { %>
                                                      <% if (file.matches(".+\\.(apk|ipa|mobileprovision)")) { %>
                                                          <li><%=file.replace(prefixPath, "")%></li>
                                                      <% } %>
                                                  <% } %>
                                              </ul>
                                          

                                          【讨论】:

                                          • 虽然它可能有效,但问题是关于文件浏览,而不是浏览文件的呈现。更好地公开您的算法,不建议将业务逻辑嵌入到 JSP 中。
                                          • 这取决于你在做什么。在企业规模的应用程序中,您是绝对正确的。如果您只需要将它作为一个简单的独立列表的插件,那么这非常好。
                                          【解决方案30】:
                                          import java.io.File;
                                          
                                          public class Main {
                                              public static void main(String[] args) {
                                                  loopFiles(new File("C:\\Users\\serge.klimkovitch\\Documents"));
                                              }
                                          
                                              private static void loopFiles(File element) {
                                                  if (element.isDirectory()) {
                                                      for (File currentFile : element.listFiles()) {
                                                          loopFiles(currentFile);
                                                          System.out.println(currentFile);
                                                      }
                                                  }
                                              }
                                          }
                                          

                                          【讨论】:

                                            猜你喜欢
                                            • 1970-01-01
                                            • 1970-01-01
                                            • 2013-05-07
                                            • 2017-11-17
                                            • 1970-01-01
                                            • 1970-01-01
                                            • 2021-03-21
                                            • 2018-05-14
                                            • 1970-01-01
                                            相关资源
                                            最近更新 更多