使用SMB/CIFS做文件服务器


placeholder image
admin 发布于:2016-07-22 09:13:47
阅读:loading

背景介绍

SMB/CIFS组件是一种Java访问共享文件夹的实现,使用它也是可以很方便的管理上传下载的文件,先闲话一些别的吧。很久以前有遇到文件的上传下载时,很“单纯”的将文件写至servletContext.getRealPath("/files");目录下,然后就没有然后了,后来慢慢的将某个服务器的共享文件夹以磁盘映射的方式作为本地的磁盘进行访问,也就是说访问时以Z:\\shares......去访问共享文件......在之前的项目中新做的文件上传下载有要求写入一个服务器上,而使用的技术实现则是从一个老系统中迁移出来的,我还一直以为用到的实现时上传的文档服务器提供的接口,并未多想,如果不是前段时间又遇到此种问题了,或许也就没有下文了。最近我找了找它还真的是一个开源组件,基于smb协议的文件访问。

一般项目当中的文档管理,基本的功能是对文档目录的列表读取、文件(夹)的增删改查以及文件的上传下载等,其最核心的包括:

1、根据目录获取目录下的所有文件(夹);

根据目录读取所有文件(夹),有了它就相当于有了文件结构的树,进一步说可以对目录及文件的增删改查了。

2、根据文件的标识获取文件的输入输出流; 

输入输出流对应了文件的上传下载,如果说文件结构管理是管理员做的事情,那么上传下载就是与普通用户交互的了,有了输出流可以下载一个文件(直接输出至浏览器);输入流可将上传的附件写入共享目录中了。

代码实现

简单的实现其实就是这么点儿了,将之前使用时的一些经验做了个简单的例子,包括文件的结构读取、文件的写入和写出,参考如下代码:

<dependency>

    <groupId>org.samba.jcifs</groupId>

    <artifactId>jcifs</artifactId>

    <version>1.3.14-kohsuke-1</version>

</dependency>

/**

 *

 */

package cn.chendd.smb;

 

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.InputStream;

import java.io.OutputStream;

import jcifs.smb.SmbFile;

 

/**

 * @author chendd

 *

 */

public class Test {

 

    /**

     * smb允许的URL为文件夹时必须以/结束,获取到的文件夹的名称仍然以/结束

     */

    private static final String SMB_SHARE_FOLDER = "smb://chendd:admincdd@127.0.0.1/smbShareTest/";

   

    /**

     * smb允许的URL为文件夹时必须以/结束

     * @throws Exception

     */

    public static void listFiles() throws Exception{

        SmbFile smbFile = new SmbFile(SMB_SHARE_FOLDER);

        SmbFile files[] = smbFile.listFiles();

        for(SmbFile file : files){

            System.out.println("名称:" + file.getName() + ",类型:" + (file.isDirectory() ? "文件夹" : "文件"));

        }

    }

   

    public static void main(String[] args) throws Exception {

        //打印共享目录下所有的文件

        System.out.println("*****打印共享文件夹下的所有一级目录*****");

        listFiles();

        System.out.println("================================");

        //将某个文件写入共享目录

        System.out.println("*****上传一个文件至共享目录*****");

        uploadFile();

        System.out.println("================================");

        System.out.println("*****从共享目录中下载一个文件*****");

        downloadFile();

    }

 

    private static void downloadFile() throws Exception{

        SmbFile smbFile = new SmbFile(SMB_SHARE_FOLDER + "jdfw.gif");

        File outputFile = new File("C:\\Users\\chendd\\Desktop\\jdfw_new.gif");

        InputStream is = smbFile.getInputStream();

        OutputStream os = new FileOutputStream(outputFile);

        byte b[] = new byte[1024];

        int len = 0;

        while((len = is.read(b)) != -1){

            os.write(b, 0, len);

        }

        os.flush();

        os.close();

        System.out.println("文件下载成功...");

    }

 

    private static void uploadFile() throws Exception {

        File file = new File("C:\\Users\\chendd\\Desktop\\jdfw.gif");

        String fileName= file.getName();

        SmbFile smbFile = new SmbFile(SMB_SHARE_FOLDER + fileName);

        InputStream is = new FileInputStream(file);

        byte b[] = new byte[1024];

        int len = 0;

        OutputStream out = smbFile.getOutputStream();

        while((len = is.read(b)) != -1){

            out.write(b, 0, len);

        }

        out.flush();

        out.close();

        System.out.println("文件上传成功...");

    }

}

运行结果

运行结果.png

当访问一个everyone的共享目录时,且不需要密码时的smb协议路径为:smb://127.0.0.1/smbShareTest/,一般情况下常常是会有账户密码的,就给出例子中的写法,本示例中的共享目录是本机的,在示例中遇到了一个smb Logon failure错误,解决方式如下:

解决方案.png

解决方案-注册表.png

总结:在读取Windows的共享目录时,可能会由于网络问题、权限问题而出现一些问题。

@补充一下

1、密码含有特殊字符的问题

一般在使用smb访问共享文件的时候,除了上述方式外还有另外一种方式,即请求的地址为:smb://ip/路径,避免账号和密码在这个地址中存在,因为密码可以是含有特殊字符的,一旦含有一些其它特殊字符直接请求是会有问题的,所以通常还会有另外一种写法:

String shareFolder = "smb://127.0.0.1/test/";

NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication("127.0.0.1", "admin", "passwor$@f/\@F"); 

SmbFile smbFileFolder = new SmbFile(shareFolder , auth);

String lists[] = smbFileFolder.list();

for (String list : lists) {

    System.out.println(list);

}

2、有时候访问的共享文件特别慢

出现这个情况是通过SmbFile获取到了一个数组对象,循环它里面的一个个具体文件的时候,导致特别慢,进程假死等情况,后来换成了在循环的时候再重新根据地址去构造一个新文件的

方式来解决,参考代码为:

BufferedInputStream bis = new BufferedInputStream(new SmbFileInputStream(....));


 点赞


 发表评论

当前回复:作者

 评论列表


留言区