关于windows主机之间传输文件,在不依赖于外部第三方服务的情况下,大多会选择 SMB/CIFS协议;关于 SMB/CIFS 协议相关的内容大家可以自行查阅微软官方文档;
目前本人测试自己的主机到 VMWare中的win7虚拟机传输文件,亲测速率最快可达到 9MB/s(具体实际使用传输速度情况,视硬件及网络带宽而定);
关键实现代码如下所示:
package com.morpheus.transfer.win;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.util.Optional;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.morpheus.transfer.common.HostVO;
import com.morpheus.transfer.utils.CommonUtil;
import jcifs.UniAddress;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbException;
import jcifs.smb.SmbFile;
import jcifs.smb.SmbFileOutputStream;
import jcifs.smb.SmbSession;
public class WinTransferService {
private static final Logger LOGGER = LoggerFactory.getLogger(WinTransferService.class);
public static final String KEY_JCIFS_SMB_CLIENT_DFS_DISABLED = "jcifs.smb.client.dfs.disabled";
private HostVO hostVO = null;
public WinTransferService(HostVO hostVO) {
this.hostVO = hostVO;
}
private boolean validParams() {
boolean notEmpty = CommonUtil.isAllNotEmpty(StringUtils.trimToEmpty(this.hostVO.getValidIP()),
StringUtils.trimToEmpty(this.hostVO.getUserName()), StringUtils.trimToEmpty(this.hostVO.getUserPwd()));
return notEmpty;
}
private boolean validParams(String... params) {
boolean notEmpty = CommonUtil.isAllNotEmpty(StringUtils.trimToEmpty(this.hostVO.getValidIP()),
StringUtils.trimToEmpty(this.hostVO.getUserName()), StringUtils.trimToEmpty(this.hostVO.getUserPwd()));
boolean notEmptyParams = CommonUtil.isAllNotEmpty(params);
return notEmpty && notEmptyParams;
}
public boolean loginToRemote() {
if (!this.validParams()) {
throw new IllegalArgumentException("Invalid host ip/userName/userPwd");
}
String domain = this.hostVO.getDomain();
String userName = this.hostVO.getUserName();
String userPwd = this.hostVO.getUserPwd();
try {
UniAddress uniDC = UniAddress.getByName(this.hostVO.getValidIP());
NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication(domain, userName, userPwd);
SmbSession.logon(uniDC, auth);
SmbFile smbFile = new SmbFile(String.format("cifs://%s/C$/", new Object[] { this.hostVO.getValidIP() }),
auth);
String[] files = smbFile.list();
files = Optional.ofNullable(files).orElse(new String[0]);
return files.length > 0;
} catch (Throwable th) {
LOGGER.error("WinTransferService.loginToRemote() Throwable", th);
}
return false;
}
public InputStream readFromRemote(String remoteFile) throws IOException {
if (!this.validParams(remoteFile)) {
throw new IllegalArgumentException("Invalid host ip/userName/userPwd");
}
String domain = this.hostVO.getDomain();
String userName = this.hostVO.getUserName();
String userPwd = this.hostVO.getUserPwd();
NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication(domain, userName, userPwd);
remoteFile = StringUtils.trimToEmpty(remoteFile);
remoteFile = remoteFile.replace("\\", "/").replace(":", "$");
SmbFile smbFile = new SmbFile(
String.format("cifs://%s/%s", new Object[] { this.hostVO.getValidIP(), remoteFile }), auth);
if (smbFile.exists()) {
return smbFile.getInputStream();
}
return new ByteArrayInputStream("ReadFromRemoteError".getBytes());
}
public void createRemoteDir(String remoteDir, boolean deleteExist, NtlmPasswordAuthentication auth)
throws MalformedURLException, SmbException {
String remoteRelativePath = remoteDir.replace("\\", "/").replace(":", "$") + "/";
String hostIP = this.hostVO.getValidIP();
SmbFile smbDir = new SmbFile(String.format("cifs://%s/%s", new Object[] { hostIP, remoteRelativePath }), auth);
if (deleteExist) {
if (smbDir.exists()) {
smbDir.delete();
}
}
if (!smbDir.exists()) {
smbDir.mkdirs();
}
}
public void transferToRemote(String localPath, String remoteDir) throws IOException {
if (!this.validParams(localPath, remoteDir)) {
throw new IllegalArgumentException("Invalid host ip/userName/userPwd");
}
File localFile = new File(localPath);
String domain = this.hostVO.getDomain();
String userName = this.hostVO.getUserName();
String userPwd = this.hostVO.getUserPwd();
LOGGER.info("WinTransferService.transferToRemote() begin: localPath={}, remoteDir={}", localPath, remoteDir);
System.setProperty(KEY_JCIFS_SMB_CLIENT_DFS_DISABLED, String.valueOf(true));
jcifs.Config.setProperty(KEY_JCIFS_SMB_CLIENT_DFS_DISABLED, String.valueOf(true));
NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication(domain, userName, userPwd);
if (localFile.isFile()) {
remoteDir = remoteDir.replace("\\", "/") + "/";
this.createRemoteDir(remoteDir, false, auth);
this.uploadFileWithSmb(localFile, remoteDir, auth);
} else if (localFile.isDirectory()) {
remoteDir = remoteDir.replace("\\", "/") + "/" + localFile.getName();
this.createRemoteDir(remoteDir, true, auth);
this.uploadDirWithSmb(localFile, remoteDir, auth);
}
LOGGER.info("WinTransferService.transferToRemote() end: localPath={}, remoteDir={}", localPath, remoteDir);
}
public void uploadFileWithSmb(File localFile, String remoteDir, NtlmPasswordAuthentication auth) {
FileInputStream fileInputStream = null;
SmbFileOutputStream smbFileOutputStream = null;
try {
String remoteRelativePath = remoteDir.replace("\\", "/").replace(":", "$") + "/" + localFile.getName();
SmbFile smbFile = new SmbFile(
String.format("cifs://%s/%s", new Object[] { this.hostVO.getValidIP(), remoteRelativePath }), auth);
smbFileOutputStream = new SmbFileOutputStream(smbFile);
fileInputStream = new FileInputStream(localFile);
IOUtils.copyLarge(fileInputStream, smbFileOutputStream);
} catch (Throwable th) {
LOGGER.error("TransferService.putFileWithSamba() Throwable", th);
} finally {
IOUtils.closeQuietly(fileInputStream);
IOUtils.closeQuietly(smbFileOutputStream);
}
}
public void uploadDirWithSmb(File localDir, String remoteDir, NtlmPasswordAuthentication auth) throws IOException {
File[] files = localDir.listFiles();
files = Optional.ofNullable(files).orElse(new File[0]);
for (File file : files) {
if (file.isDirectory()) {
String relativeDestFile = remoteDir.replace("\\", "/").replace(":", "$") + "/" + file.getName();
SmbFile smbFile = new SmbFile(
String.format("cifs://%s/%s", new Object[] { this.hostVO.getValidIP(), relativeDestFile }),
auth);
if (!smbFile.exists()) {
smbFile.mkdirs();
} else if (smbFile.isDirectory()) {
smbFile.delete();
}
this.uploadDirWithSmb(file, remoteDir + "/" + file.getName(), auth);
} else if (file.isFile()) {
FileInputStream fileInputStream = null;
SmbFileOutputStream smbFileOutputStream = null;
try {
String relativeDestFile = remoteDir.replace("\\", "/").replace(":", "$") + "/" + file.getName();
SmbFile smbFile = new SmbFile(
String.format("cifs://%s/%s", new Object[] { this.hostVO.getValidIP(), relativeDestFile }),
auth);
smbFileOutputStream = new SmbFileOutputStream(smbFile);
fileInputStream = new FileInputStream(file);
IOUtils.copyLarge(fileInputStream, smbFileOutputStream);
} finally {
IOUtils.closeQuietly(fileInputStream);
IOUtils.closeQuietly(smbFileOutputStream);
}
}
}
}
}
可以将文件传输封装成异步任务,以便多线程上传,如下所示:
package com.morpheus.transfer.win;
import java.util.concurrent.Callable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import com.alibaba.fastjson.JSONObject;
import com.morpheus.transfer.common.HostVO;
import com.morpheus.transfer.utils.JSONUtil;
public class TransferJob implements Callable<JSONObject> {
private static final Logger LOGGER = LoggerFactory.getLogger(TransferJob.class);
private WinTransferService winTransferService = null;
private String localDir = null;
private String remoteDir = null;
public TransferJob(HostVO hostVO, String localDir, String remoteDir) {
this.winTransferService = new WinTransferService(hostVO);
this.localDir = localDir;
this.remoteDir = remoteDir;
}
@Override
public JSONObject call() throws Exception {
JSONObject resJSON = new JSONObject();
try {
boolean login = this.winTransferService.loginToRemote();
if (login) {
this.winTransferService.transferToRemote(this.localDir, this.remoteDir);
resJSON = JSONUtil.createResult(resJSON, true, HttpStatus.OK.value(), "TransferFinished");
}
} catch (Throwable th) {
LOGGER.error("TransferJob.call() Throwable", th);
resJSON = JSONUtil.createResult(resJSON, true, HttpStatus.OK.value(), "TransferError");
}
return resJSON;
}
}