Java中SMB的相關應用
SMB簡介
SMB(全稱是Server Message Block)是一個協議名,它能被用於Web連接和客戶端與服務器之間的信息溝通。SMB協議作為一種局域網文件共享傳輸協議,常被用來作為共享文件安全傳輸研究的平台。
Windows操作系統都包括了客戶機和服務器SMB協議支持。Microsoft為 Internet提供了SMB的開源版本,即通用Internet文件系統CIFS。與現有Internet應用程序如文件傳輸協議FTP相比, CIFS靈活性更大。對於UNIX系統,可使用一種稱為Samba的共享軟件。
引入SMB依賴
在pom.xml
中引入SMB服務相關的依賴:
<!-- 引用SmbFile類的jar包 -->
<dependency>
<groupId>jcifs</groupId>
<artifactId>jcifs</artifactId>
<version>1.3.17</version>
</dependency>
路徑格式
在Java中通過SMB訪問遠程共享目錄的請求格式有如下三種情況:(以test
共享文件夾下的test.txt
文件示例)
- 如果是無需密碼的共享,格式類似:
smb://ip/sharefolder/filename
(例如:smb://192.168.1.106/test/test.txt
) - 如果需要用戶名和密碼,格式類似:
smb://username:password@ip/sharefolder/filename
(例如:smb://admin:damin@192.168.1.106/test/test.txt
) - 如果需要用戶名密碼和域名,格式類似:
smb:host;username:password@ip/sharefolder/filename
(例如:smb://orcl;admin:admin@192.168.1.106/test/test.txt
)
上傳下載
具備了以上環節的准備,就可以在項目中實現SMB相關的應用;下面是一個簡單的從共享文件夾目錄中將文件下載到指定文件夾的方法。
package com.example.smb;
import jcifs.smb.SmbFile;
import jcifs.smb.SmbFileInputStream;
import org.springframework.util.FileCopyUtils;
import java.io.*;
/**
* @author: Create By 成猿手冊
* @description: Java中SMB的上傳和下載
* @date: 2020/03/22
*/
public class Demo {
private static final String SMB_SHARE_FOLDER = "smb://admin:admin@192.168.1.106/Test/";
private static final String SHARE_FOLDER_PATH = "2020-03-21\\result";
private static final String FILE_NAME = "myresult.txt";
private static final String LOCAL_DIR = "D:\\LocalTest";
public static void main(String[] args) {
downloadSmbFile(SMB_SHARE_FOLDER, SHARE_FOLDER_PATH, FILE_NAME, LOCAL_DIR);
}
/**
* 從SMB共享文件夾下載文件到本地
* @param smburl smb請求的url
* @param shareFolder 共享文件夾中目標文件存放的完整路徑
* @param fileName 要下載/上傳的完整文件名
* @param localDir 要上傳/下載的完整文件夾路徑
*/
public static void downloadSmbFile(String smburl, String shareFolder, String fileName, String localDir) {
InputStream in = null;
OutputStream out = null;
try {
SmbFile smbfile =
new SmbFile(smburl + shareFolder + File.separator + fileName);
File localFile = new File(localDir + File.separator + fileName);
//文件上傳到SMB共享文件目錄與該寫法類似;即使用SmbFileOutputStream(smbfile);
in = new BufferedInputStream(new SmbFileInputStream(smbfile));
out = new BufferedOutputStream(new FileOutputStream(localFile));
FileCopyUtils.copy(in, out);
} catch (Exception e) {
e.printStackTrace();
} finally {
closeStreanm(in, out);
}
}
//關閉文件流
private static void closeStreanm(InputStream in, OutputStream out) {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
不難發現,對SMB共享文件的操作放在Java中其實就是轉換為操作SmbFile
這個對象,一旦成功構建(合法的url
,正確的canon
等必要屬性)該對象,許多問題也就變得簡單起來。
例如,自己工作中有一個業務需求是要檢測SMB共享目錄中的某個文件是否存在,代碼示例如下:
/**
* 檢測SMB共享文件夾中的文件是否存在
* @param smburl smb請求的url
* @param shareFolder 共享文件夾中目標文件存放的完整路徑
* @param fileName 要檢測文件的完整文件名
*/
public static boolean checkSmbFile(String smburl, String shareFolder, String fileName) {
boolean result = false;
try {
SmbFile smbfile =
new SmbFile(smburl + shareFolder + File.separator + fileName);
result = smbfile.exists();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
登錄驗證
在這里使用登錄驗證主要是為解決賬號密碼中含有特殊字符的情況(比如轉義字符,鏈接里的特定字符),存在特殊字符的賬號密碼再使用上面的路徑去請求SMB服務往往會報出下列異常:
Connected to the target VM, address: '127.0.0.1:54593', transport: 'socket'
jcifs.smb.SmbAuthException: Logon failure: unknown user name or bad password.
這時為了構建合法的SmbFile
對象,我們就換一種思路:先進行登錄驗證,再去嘗試構建該對象:
package com.example.smb;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbFile;
import java.net.MalformedURLException;
/**
* @author: Create By 成猿手冊
* @description: smb賬號密碼中含有特殊字符的處理方法
* @date: 2020/3/22
*/
public class SmbFileHelper {
public static SmbFile newSmbFile(SmbFileInfo smbFileInfo) throws MalformedURLException {
NtlmPasswordAuthentication auth =
new NtlmPasswordAuthentication(smbFileInfo.getIp(), smbFileInfo.getUsername(), smbFileInfo.getPassword());
String smburl = String.format("smb://%s/%s", smbFileInfo.getIp(), smbFileInfo.getFilepath());
return new SmbFile(smburl, auth);
}
}
SmbFileInfo
類的寫法:
package com.example.smb;
/**
* @author: Create By 成猿手冊
* @description: SmbFileInfo實體類
* @date: 2020/3/22
*/
public class SmbFileInfo {
private String ip;
private String username;
private String password;
private String filepath;
//這里省略了get()和set()方法
}