【问题标题】:Convert SmbFile to Java IO File将 SmbFile 转换为 Java IO 文件
【发布时间】:2016-07-20 06:24:38
【问题描述】:

我的 Java 应用程序需要访问保存在远程共享文件夹中的大型 Excel 文件(大小超过 1GB)。我正在使用 SmbFile 来获取具有身份验证的文件。

注意:主要出于大小原因,不能下载文件。

问题是我需要将 excel 文件作为 Java IO 文件而不是 SmbFile,因为我用来解析 excel 的 other libraries 只接受 Java IO 文件。

  1. 有没有办法将此 SmbFile 转换为 Java 兼容文件?

【问题讨论】:

  • 其他库是否接受除File 以外的任何其他内容?例如。 InputStream?如果是,您可以使用SmbFile.getInputStream()。如果不是,您可以在本地下载文件(例如到临时文件)并将其用于其他库。这对你有用吗?
  • 该库与 InputStream 一起使用,但问题是它在处理每个 excel 工作表后也会关闭流,然后我必须为下一张工作表再次打开它,我认为应该没问题。由于 excel 的大小 (1GB+),下载文件不是一个选项。我会试试 InputStream 并告诉你。
  • 你能包括你用于excel解析的库吗?也许这会带来更多的人来提供帮助。
  • 如果只是 close() 调用有问题,那么您可能希望使用拦截关闭并忽略它的虚拟实现来包装 InputSTream。谨防!这是一个相当的技巧,只有在没有其他解决方案并且重新打开代价高昂或痛苦时才建议作为“最后的手段”。
  • @mad_manny 我在 apache poi (github.com/monitorjbl/excel-streaming-reader) 上使用了一个包装器。它接受文件和输入流。我想我应该使用输入流而不是文件。

标签: java smb


【解决方案1】:

使用 Apache Commons IO

https://mvnrepository.com/artifact/commons-io/commons-io

NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication("", "user", "key");

SmbFile smbFile = new SmbFile("smb://IP/pitoka.tmp", auth)

InputStream initialStream = smbFile.getInputStream();

File targetFile = new File("/tmp/pitoka.tmp");

FileUtils.copyInputStreamToFile(initialStream, targetFile);

希望对你有帮助。

【讨论】:

    【解决方案2】:

    我猜这只是结构问题,使用 SmbFile 我们有两个参数,而使用 File 我们只有一个参数。 所以,我的想法是声明一个与 SmbFile 相同路径的文件并尝试处理您的文件。 例如,在我想递归地删除我的文件夹的内容:

    SmbFile sFile = new SmbFile(path, auth)
    
    if (sFile.exists()) {
     File file = new File(path);
     deleteDirectory(file);
    

    }

     boolean deleteDirectory(File directoryToBeDeleted) {
        File[] allContents = directoryToBeDeleted.listFiles();
        if (allContents != null) {
            for (File file : allContents) {
                deleteDirectory(file);
            }
        }
        return directoryToBeDeleted.delete();
    }
    

    我希望这种和平的代码对你有帮助,对不起我的英语!

    【讨论】:

      【解决方案3】:
          jcifs.smb.SmbFile smbFile = new SmbFile("smb://host/fileShare/.../file");
          java.io.File javaFile = new File(smbFile.getUncPath());
      
          System.out.println(smbFile);
          System.out.println(javaFile);
      

      输出

      smb://host/fileShare/.../file
      \\host\fileShare\...\file
      

      smbFile.getUncPath() 的 javadoc 说

      使用反斜杠而不是正向重新调整 Windows UNC 样式路径 斜线。

      我在 Windows 10 上使用 jcifs-1.3.17.jar。

      【讨论】:

        【解决方案4】:

        最近我也遇到了类似的情况,但是我在网上没有找到好的解决方案,但是我写了一个基本的代码,可以轻松完成我需要的事情。

        在您的情况下,您需要使用带有身份验证的 SmbFile 从源(远程目录)复制 excel 文件到目标(本地目录),然后才转换目标的 excel 文件路径(getCanonicalPath() 函数) 并使用下面的代码将其从 SmbFile 格式转换为 File 格式。 之后,使用文件目标路径创建您的 File 对象并执行您想要的操作。

        我使用JCIFS 使用SMBFILE 类来处理远程共享目录。

        首先需要导入主库:

        import java.io.File;
        import java.io.IOException;
        import jcifs.smb.SmbFile;
        

        其次,需要创建一个静态方法,将SmbFile格式转换为File格式:

        /**
         * This method convert a directory path from SmbFile format to File format.<br />
         * <p><strong>Sintax:</strong> <br />&nbsp;&nbsp;&nbsp;&nbsp;convertSmbFileToFile("Canonical Path")</p>
         * <p><strong>Example:</strong> <br />&nbsp;&nbsp;&nbsp;&nbsp;convertSmbFileToFile("smb://localhost/D$/DOCUMENTOS/workspace/tests2/access")</p>
         * @param smbFileCanonicalPath String
         * @see String
        */
        public static String convertSmbFileToFile(String smbFileCanonicalPath) {
            String[] tempVar = smbFileCanonicalPath.substring(6).replace("$", ":").split("/"); 
            String bar = "\\";
            String finalDirectory = "";
            for (int i = 1; i < tempVar.length; i++) {
                finalDirectory += tempVar[i] + bar;
                if (i == tempVar.length - 1) {
                    finalDirectory = finalDirectory.substring(0,finalDirectory.length()-1);
                }
            }
            return finalDirectory;
        }
        

        可选,您也可以创建一个静态方法将 File 格式转换为 SmbFile 格式:

        /**
         * This method convert a directory path from File format to SmbFile format.<br />
         * <p><strong>Sintax:</strong> <br />&nbsp;&nbsp;&nbsp;&nbsp;convertFileToSmbFile("Canonical Path")</p>
         * <p><strong>Example:</strong> <br />&nbsp;&nbsp;&nbsp;&nbsp;convertFileToSmbFile("D:\DOCUMENTOS\workspace\tests2\access")</p>
         * @param fileCanonicalPath String
         * @see String
        */
        public static String convertFileToSmbFile(String fileCanonicalPath) {
            return "smb://localhost/" + fileCanonicalPath.toString().replace(":", "$").replace("\\", "/");
        }
        

        最后,你可以像下面的例子那样调用方法:

        String dirDest = "access/";
        
            try {
        
                File localDirFile = new File(dirDest);
        
                SmbFile localSmbDirFile = new SmbFile(convertFileToSmbFile(localDirFile.getCanonicalPath()));
                File localDirFile2 = new File(convertSmbFileToFile(localSmbDirFile.getCanonicalPath()));
        
                System.out.println("Original File Format: " + localDirFile.getCanonicalPath());
                System.out.println("Original File Format to SmbFile Format: " + localSmbDirFile.getCanonicalPath());
                System.out.println("Converted SmbFile Format to File Format: " + localDirFile2.getCanonicalPath());
        
            } catch (IOException e) {
        
                System.err.println("[ERR] IO Exception - " + e);
        
            }
        

        之前代码运行的结果:

        Original File Format: D:\DOCUMENTOS\workspace\tests2\access
        Original File Format to SmbFile Format: smb://localhost/D$/DOCUMENTOS/workspace/tests2/access
        Converted SmbFile Format to File Format: D:\DOCUMENTOS\workspace\tests2\access
        

        额外信息:getCanonicalPath()

        也许这段代码会对你有所帮助,如果你愿意,我可以讨论。

        祝你好运!

        【讨论】:

          【解决方案5】:

          查看您图书馆的implementation details

          这个库将接受一个提供的 InputStream 并将其输出到文件系统。 (...) 创建文件后,它会从文件系统流式传输到内存中。

          需要以这种方式输出流的原因与 ZIP 文件的工作方式有关。因为 XLSX 文件格式基本上是一个 ZIP 文件,所以如果不读取整个 InputStream 就不可能找到所有条目。

          (...) 这个库通过将流读出到一个临时文件中来工作。作为自动关闭操作的一部分,临时文件将被删除。

          如果您需要更多地控制文件的创建/处置方式,可以选择使用java.io.File 初始化库。此文件不会被写入或删除

          所以无论您使用File 还是InputStream API 都没关系 - 无论如何都需要下载整个文件。

          最简单的解决方案是将SmbFile.getInputStream() 传递给

          StreamingReader.builder().read(smbFile.getInputStream())
          

          但您也可以先下载文件,例如。通过IOUtils.copy()Files.copy()

          File file = new File("...");
          try (
               in = smbFile.getInputStream();
               out = new FileOutputStream(file)
          ) {
              IOUtils.copy(in, out);
          }
          

          try (in = smbFile.getInputStream()) {
              Files.copy(smbFile.getInputStream(), file.toPath());
          }
          

          并将file 传递给

          StreamingReader.builder().read(file)
          

          【讨论】:

          • 非常感谢您花时间检查该库。让我在这里解释一下主要问题。我无法在本地服务器上复制文件,因为文件大小会增加时间(必须对 1kk+ 行和 338 列进行大量验证检查,因此无法下载文件。
          • 我不想使用 inputStream 并将其传递给该库的原因是它没有提供任何方法来知道在阅读之前有多少张纸,它只是告诉你如果如果您提供它的名称并且我需要张数,则可以使用工作表。所以我的解决方法是解压缩文件并检查工作表文件夹和计数,这可以使用 Apache POI 轻松完成,但由于文件大小,使用 InputStream 会引发异常,因此只有当文件可以读取为Java 文件
          • 我正在尝试将文件完全托管在我的服务器文件夹中,以便对其进行本地访问,这将简化它的一切。但再次感谢您的帮助
          • @arashmoeen - 有趣的用例。除了在文件实际驻留的服务器上进行这种处理之外,我想不出更好的选择 - 正如您所说。祝你好运!
          猜你喜欢
          • 1970-01-01
          • 2020-04-26
          • 1970-01-01
          • 1970-01-01
          • 2014-11-12
          • 2017-03-18
          • 2015-05-17
          • 2013-06-05
          • 2011-05-13
          相关资源
          最近更新 更多