写在前面,由于项目要求负载,又不想大动干戈采用比较贵的设备和高大上的框架,经过一番研究,想使用文件共享方式实现文件的跨服务器访问。本方案采用了jcifs和smbj框架,若想用,请自行查找资源。此为初步设计方案,若接口不全,请自行完善。通用类采用抽象类继承的方式,而不是接口实现(总觉得此处不适合使用接口);采用了单例模式。写的不好的地方,请各位大拿批评指正。

1、引入项目依赖 pom.xml

 <!-- SMB共享1.0 -->
<dependency>
  <groupId>org.jcifs</groupId>
  <artifactId>jcifs</artifactId>
  <version>1.3.17</version>
</dependency>
<!-- SMB共享2、3 https://mvnrepository.com/artifact/com.hierynomus/smbj -->
<dependency>
  <groupId>com.hierynomus</groupId>
  <artifactId>smbj</artifactId>
  <version>0.11.5</version>
</dependency>

2、共享相关配置 application.yml

#smb 文件共享配置

smbshare:
#是否启用
enabled: true
#共享服务器地址 IP或其他
domain: '192.168.192.100'
#共享用户名
username: xxxx
#共享密码
password: ******
#smb 文件共享版本,默认1.0
version: 1
#smb 文件共享名称,版本2、3使用 为本地资源目录映射地址配置第一个目录相同
sharename: resshare

  

3、配置类



/**
* 读取项目smb文件共享相关配置
*
* @author wgp
*/
@Component
@ConfigurationProperties(prefix = "smbshare")
public class SmbShareConfig {
// @Value("${smbshare.enabled}")

/**
* 是否启用
*/
private static Boolean enabled;
/**
* 共享服务器地址
*/
private static String domain;
/**
* 用户名
*/
private static String username;
/**
* 密码
*/
private static String password;

/**
* smb文件共享版本 1、2、3
*/
private static String version;

/**
* 共享名称 2、3使用
*/
// @Value("${smbshare.sharename}")
private static String sharename;

public static Boolean getEnabled() {
if (enabled == null) enabled = new Boolean(false);
return enabled;
}

public void setEnabled(Boolean enabled) {
SmbShareConfig.enabled = enabled;
}

public static String getUsername() {
return username;
}

public void setUsername(String username) {
SmbShareConfig.username = username;
}

public static String getPassword() {
return password;
}

public void setPassword(String password) {
SmbShareConfig.password = password;
}

public static String getDomain() {
return domain;
}

public void setDomain(String domain) {
this.domain = domain;
}

public static String getVersion() {
if (version == null) version = "1";
return version;
}

public void setVersion(String version) {
this.version = version;
}

public static String getShareName(String defaultName) {
return (sharename == null || sharename == "") ? defaultName : sharename;
}

public void setShareName(String shareName) {
this.sharename = shareName;
}
}
 

4、上传文件通用类

public abstract class ShareFileUploadUtils extends ShareFileUtilsParent {
private static volatile ShareFileUploadUtils instance = null; /**
* 获取实例
*
* @return
*/
public static ShareFileUploadUtils getInstance() {
if (instance == null) {
synchronized (ShareFileUploadUtils.class) {
if (instance == null) {
if (SmbShareConfig.getEnabled()) {
if (SmbShareConfig.getVersion() == "1") {
instance = new ShareSmbFileUploadUtilsImpl();
} else {
instance = new ShareSmbJFileUploadUtilsImpl();
}
} else {
instance = new ShareFileUploadUtilsImpl();
}
}
}
}
return instance;
} public final String getDefaultBaseDir() {
return FileUploadUtils.getDefaultBaseDir();
} /**
* 文件上传
*
* @param file 上传的文件
* @return 文件名称
* @throws Exception
*/
public String upload(MultipartFile file) throws IOException {
throw new IOException("方法未实现,请使用子类");
} /**
* 根据文件路径上传
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @return 文件名称
* @throws IOException
*/
public String upload(String baseDir, MultipartFile file) throws IOException {
throw new IOException("方法未实现,请使用子类");
} /**
* 根据文件路径上传到安全目录
*
* @param baseDir
* @param file
* @return
* @throws IOException
*/
public String uploadSafety(String baseDir, MultipartFile file) throws IOException {
throw new IOException("方法未实现,请使用子类");
} /**
* 文件上传
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @param allowedExtension 上传文件类型
* @return 返回上传成功的文件名
* @throws FileSizeLimitExceededException 如果超出最大大小
* @throws FileNameLengthLimitExceededException 文件名太长
* @throws IOException 比如读写文件出错时
* @throws InvalidExtensionException 文件校验异常
*/
public String upload(String baseDir, MultipartFile file, String[] allowedExtension)
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
InvalidExtensionException {
throw new IOException("方法未实现,请使用子类");
} public String uploadSafety(String baseDir, MultipartFile file, String[] allowedExtension)
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
InvalidExtensionException {
throw new IOException("方法未实现,请使用子类");
} /**
* 编码文件名
*/
public final String extractFilename(MultipartFile file) {
return FileUploadUtils.extractFilename(file);
} public final String getPathFileName(String uploadDir, String fileName) throws IOException {
return FileUploadUtils.getPathFileName(uploadDir, fileName);
} public final String getSafetyPathFileName(String uploadDir, String fileName) throws IOException {
return FileUploadUtils.getSafetyPathFileName(uploadDir, fileName);
} /**
* 文件大小校验
*
* @param file 上传的文件
* @return
* @throws FileSizeLimitExceededException 如果超出最大大小
* @throws InvalidExtensionException
*/
protected void assertAllowed(MultipartFile file, String[] allowedExtension)
throws IOException, FileSizeLimitExceededException, InvalidExtensionException {
FileUploadUtils.assertAllowed(file, allowedExtension);
} /**
* 判断MIME类型是否是允许的MIME类型
*
* @param extension
* @param allowedExtension
* @return
*/
public final boolean isAllowedExtension(String extension, String[] allowedExtension) {
return FileUploadUtils.isAllowedExtension(extension, allowedExtension);
} /**
* 获取文件名的后缀
*
* @param file 表单文件
* @return 后缀名
*/
public final String getExtension(MultipartFile file) {
return FileUploadUtils.getExtension(file);
}
} class ShareFileUploadUtilsImpl extends ShareFileUploadUtils {
/**
* 文件上传 实现
*
* @param file 上传的文件
* @return 文件名称
* @throws Exception
*/
@Override
public String upload(MultipartFile file) throws IOException {
try {
return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
} /**
* 文件上传
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @param allowedExtension 上传文件类型
* @return 返回上传成功的文件名
* @throws FileSizeLimitExceededException 如果超出最大大小
* @throws FileNameLengthLimitExceededException 文件名太长
* @throws IOException 比如读写文件出错时
* @throws InvalidExtensionException 文件校验异常
*/
@Override
public String upload(String baseDir, MultipartFile file, String[] allowedExtension)
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
InvalidExtensionException {
return FileUploadUtils.upload(baseDir, file, allowedExtension);
} /**
* 根据文件路径上传
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @return 文件名称
* @throws IOException
*/
@Override
public String upload(String baseDir, MultipartFile file) throws IOException {
try {
return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
} /**
* 文件上传到安全目录
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @param allowedExtension 上传文件类型
* @return 返回上传成功的文件名
* @throws FileSizeLimitExceededException 如果超出最大大小
* @throws FileNameLengthLimitExceededException 文件名太长
* @throws IOException 比如读写文件出错时
* @throws InvalidExtensionException 文件校验异常
*/
@Override
public String uploadSafety(String baseDir, MultipartFile file, String[] allowedExtension)
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
InvalidExtensionException {
return FileUploadUtils.uploadSafety(baseDir, file, allowedExtension);
} @Override
public String uploadSafety(String baseDir, MultipartFile file) throws IOException {
try {
return uploadSafety(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
} } class ShareSmbFileUploadUtilsImpl extends ShareFileUploadUtils { /**
* 文件上传
*
* @param file 上传的文件
* @return 文件名称
* @throws Exception
*/
@Override
public String upload(MultipartFile file) throws IOException {
try {
return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
} /**
* 文件上传
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @param allowedExtension 上传文件类型
* @return 返回上传成功的文件名
* @throws FileSizeLimitExceededException 如果超出最大大小
* @throws FileNameLengthLimitExceededException 文件名太长
* @throws IOException 比如读写文件出错时
* @throws InvalidExtensionException 文件校验异常
*/
@Override
public String upload(String baseDir, MultipartFile file, String[] allowedExtension)
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
InvalidExtensionException {
int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
}
assertAllowed(file, allowedExtension);
String fileName = extractFilename(file);
saveSmbFile(file, baseDir + "/" + fileName);
return getPathFileName(baseDir, fileName);
} /**
* 根据文件路径上传
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @return 文件名称
* @throws IOException
*/
@Override
public String upload(String baseDir, MultipartFile file) throws IOException {
try {
return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
} /**
* 文件上传到安全目录
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @param allowedExtension 上传文件类型
* @return 返回上传成功的文件名
* @throws FileSizeLimitExceededException 如果超出最大大小
* @throws FileNameLengthLimitExceededException 文件名太长
* @throws IOException 比如读写文件出错时
* @throws InvalidExtensionException 文件校验异常
*/
@Override
public String uploadSafety(String baseDir, MultipartFile file, String[] allowedExtension)
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
InvalidExtensionException {
int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
} assertAllowed(file, allowedExtension); String fileName = extractFilename(file);
saveSmbFile(file, baseDir + "/" + fileName);
return getSafetyPathFileName(baseDir, fileName);
} @Override
public String uploadSafety(String baseDir, MultipartFile file) throws IOException {
try {
return uploadSafety(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
} /**
* 保存文件
*
* @param file
* @param filePath
* @throws IOException
*/
private void saveSmbFile(MultipartFile file, String filePath) throws IOException {
InputStream inputStream = null;
SmbFileOutputStream outputStream = null;
try {
inputStream = file.getInputStream();
SmbFile smbFile = getAbsoluteSmbFile(filePath);
outputStream = new SmbFileOutputStream(smbFile);
outputStream.write(file.getBytes());
// byte[] buffer = new byte[4096];
// int len = 0;
// while ((len = inputStream.read(buffer, 0, buffer.length)) != -1) {
// outputStream.write(buffer, 0, len);
// }
} catch (IOException e) {
throw e;
} finally {
IOUtils.close(inputStream);
IOUtils.close(outputStream);
}
} } class ShareSmbJFileUploadUtilsImpl extends ShareFileUploadUtils { public ShareSmbJFileUploadUtilsImpl() {
session = getSmbjSession();
} private Session session; /**
* 以默认配置进行文件上传
*
* @param file 上传的文件
* @return 文件名称
* @throws Exception
*/
@Override
public String upload(MultipartFile file) throws IOException {
try {
return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
} /**
* 文件上传
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @param allowedExtension 上传文件类型
* @return 返回上传成功的文件名
* @throws FileSizeLimitExceededException 如果超出最大大小
* @throws FileNameLengthLimitExceededException 文件名太长
* @throws IOException 比如读写文件出错时
* @throws InvalidExtensionException 文件校验异常
*/
@Override
public String upload(String baseDir, MultipartFile file, String[] allowedExtension)
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
InvalidExtensionException {
int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
}
assertAllowed(file, allowedExtension);
String fileName = extractFilename(file);
writeFile(file.getBytes(), baseDir + "/" + fileName);
return getPathFileName(baseDir, fileName);
} /**
* 根据文件路径上传
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @return 文件名称
* @throws IOException
*/
@Override
public String upload(String baseDir, MultipartFile file) throws IOException {
try {
return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
} /**
* 文件上传到安全目录
*
* @param baseDir 相对应用的基目录
* @param file 上传的文件
* @param allowedExtension 上传文件类型
* @return 返回上传成功的文件名
* @throws FileSizeLimitExceededException 如果超出最大大小
* @throws FileNameLengthLimitExceededException 文件名太长
* @throws IOException 比如读写文件出错时
* @throws InvalidExtensionException 文件校验异常
*/
@Override
public String uploadSafety(String baseDir, MultipartFile file, String[] allowedExtension)
throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
InvalidExtensionException {
int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) {
throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
}
assertAllowed(file, allowedExtension);
String fileName = extractFilename(file);
writeFile(file.getBytes(), baseDir + "/" + fileName);
return getSafetyPathFileName(baseDir, fileName);
} @Override
public String uploadSafety(String baseDir, MultipartFile file) throws IOException {
try {
return uploadSafety(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
} catch (Exception e) {
throw new IOException(e.getMessage(), e);
}
} /**
* 写入数据到文件
*
* @param data
* @param filePath
* @throws IOException
*/
private void writeFile(byte[] data, String filePath) throws IOException {
OutputStream fis = null;
try {
String[] filePathArr = getShareNameAndFileName(filePath);
if (filePathArr == null) throw new IOException("未输入文件路径");
DiskShare diskShare = (DiskShare) session.connectShare(SmbShareConfig.getShareName(filePathArr[0]));
if (!diskShare.fileExists(filePathArr[1])) {
//创建
String parentPath = filePathArr[1].substring(0, filePathArr[1].lastIndexOf("/"));
if (!diskShare.folderExists(parentPath)) {
diskShare.mkdir(parentPath);
}
}
com.hierynomus.smbj.share.File file = diskShare.openFile(filePathArr[1],
EnumSet.of(AccessMask.GENERIC_ALL),
(Set) null,
SMB2ShareAccess.ALL,
SMB2CreateDisposition.FILE_OPEN,
(Set) null
);
fis = file.getOutputStream();
fis.write(data);
} catch (IOException e) {
throw e;
} finally {
IOUtils.close(fis);
}
} }

5、文件通用类

/**
* 文件操作处理类型,支持共享
*/
public abstract class ShareFileUtils extends ShareFileUtilsParent {

private static volatile ShareFileUtils instance = null; /**
* 获取实例
*
* @return
*/
public static ShareFileUtils getInstance() {
if (instance == null) {
synchronized (ShareFileUtils.class) {
//考虑单例模式?
if (instance == null) {
if (SmbShareConfig.getEnabled()) {
if (SmbShareConfig.getVersion() == "1") {
instance = new ShareSmbFileUtilsImpl();
} else {
instance = new ShareSmbJFileUtilsImpl();
} } else {
instance = new ShareFileUtilsImpl();
}
}
}
}
return instance;
} /**
* 判断文件是否存在
*
* @param filePath
* @return
* @throws IOException
*/
public boolean fileExists(String filePath) throws IOException {
throw new IOException("未实现方法,请使用子类");
} /**
* 输出指定文件的byte数组
*
* @param filePath 文件路径
* @param os 输出流
* @return
*/
public void writeBytes(String filePath, OutputStream os) throws IOException {
throw new IOException("未实现方法,请使用子类");
} /**
* 写数据到文件中
*
* @param data 数据
* @return 目标文件
* @throws IOException IO异常
*/
public String writeImportBytes(byte[] data) throws IOException {
throw new IOException("未实现方法,请使用子类");
} /**
* 写数据到文件中
*
* @param data 数据
* @param uploadDir 目标文件
* @return 目标文件
* @throws IOException IO异常
*/
public String writeBytes(byte[] data, String uploadDir) throws IOException {
throw new IOException("未实现方法,请使用子类");
} /**
* 删除文件
*
* @param filePath 文件
* @return
*/
public boolean deleteFile(String filePath) throws IOException {
throw new IOException("未实现方法");
} /**
* 文件名称验证
*
* @param filename 文件名称
* @return true 正常 false 非法
*/
public boolean isValidFilename(String filename) {
return FileUtils.isValidFilename(filename);
} /**
* 下载文件名重新编码
*
* @param request 请求对象
* @param fileName 文件名
* @return 编码后的文件名
*/
public String setFileDownloadHeader(HttpServletRequest request, String fileName) throws
UnsupportedEncodingException {
return FileUtils.setFileDownloadHeader(request, fileName);
} /**
* 下载文件名重新编码
*
* @param response 响应对象
* @param realFileName 真实文件名
*/
public void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws
UnsupportedEncodingException {
FileUtils.setAttachmentResponseHeader(response, realFileName);
} /**
* 百分号编码工具方法
*
* @param s 需要百分号编码的字符串
* @return 百分号编码后的字符串
*/
public String percentEncode(String s) throws UnsupportedEncodingException {
return FileUtils.percentEncode(s);
} /**
* 获取图像后缀
*
* @param photoByte 图像数据
* @return 后缀名
*/
public String getFileExtendName(byte[] photoByte) {
return FileUtils.getFileExtendName(photoByte);
} public final File getAbsoluteFile(String uploadDir, String fileName) throws IOException {
return FileUploadUtils.getAbsoluteFile(uploadDir, fileName);
} } /**
* ShareFileUtils 和 ShareFileUploadUtils 共用基类 不允许创建
*/
abstract class ShareFileUtilsParent {
/**
* 转换可访问路径,自动适配默认目录或安全目录
*
* @param fileUrl 带有虚拟目录前缀的 文件地址,不带有http或https
* @return
*/
public final String convertToDiskPath(String fileUrl) {
if (StringUtils.isEmpty(fileUrl)) return fileUrl;
String asbPath = "";
String prePath = ""; String prePathReplace = "";
if (fileUrl.startsWith(Constants.RESOURCE_PREFIX)) {
prePath = Constants.RESOURCE_PREFIX;
prePathReplace = ApplicationConfig.getProfile();
} else if (fileUrl.startsWith(Constants.RESOURCE_SAFETYPREFIX)) {
prePath = Constants.RESOURCE_SAFETYPREFIX;
prePathReplace = ApplicationConfig.getSafetyProfile();
}
if (StringUtils.isEmpty(prePath) || StringUtils.isEmpty(prePathReplace)) return fileUrl; int dirLastIndex = prePath.length();
String subPath = StringUtils.substring(fileUrl, dirLastIndex); return (prePathReplace.endsWith("/") ? prePathReplace.substring(0, prePathReplace.length() - 1) : prePathReplace)
+ "/" +
(subPath.startsWith("/") ? subPath.substring(1) : subPath);
} /**
* 转换为Web访问路径(带有虚拟目录前缀的文件地址,不带有http或https)
* 自动适配默认目录或安全目录
*
* @param diskPath 磁盘文件地址
* @return
*/
public final String convertToWebUrl(String diskPath) {
if (StringUtils.isEmpty(diskPath)) return diskPath;
String prePath = "";
String prePathReplace = "";
if (diskPath.startsWith(ApplicationConfig.getProfile())) {
prePath = ApplicationConfig.getProfile();
prePathReplace = Constants.RESOURCE_PREFIX;
} else if (diskPath.startsWith(Constants.RESOURCE_SAFETYPREFIX)) {
prePath = ApplicationConfig.getSafetyProfile();
prePathReplace = Constants.RESOURCE_SAFETYPREFIX;
}
if (StringUtils.isEmpty(prePath) || StringUtils.isEmpty(prePathReplace)) return diskPath; int dirLastIndex = prePath.length();
String subPath = StringUtils.substring(diskPath, dirLastIndex); return (prePathReplace.endsWith("/") ? prePathReplace.substring(0, prePathReplace.length() - 1) : prePathReplace)
+ "/" +
(subPath.startsWith("/") ? subPath.substring(1) : subPath);
} /**
* 获取文件名称
*
* @param fileName 路径名称
* @return 没有文件路径的名称
*/
public final String getName(String fileName) {
if (fileName == null) {
return null;
}
String tempFile = fileName;
int quesIndex = tempFile.indexOf("?");
int dotIndex = tempFile.lastIndexOf(".");
if (quesIndex > 0 && quesIndex > dotIndex) {
tempFile = fileName.substring(0, quesIndex - 1);
}
int lastUnixPos = tempFile.lastIndexOf('/');
int lastWindowsPos = tempFile.lastIndexOf('\\');
int index = Math.max(lastUnixPos, lastWindowsPos);
return tempFile.substring(index + 1);
} /**
* 检查文件是否可下载
*
* @param resource 需要下载的文件
* @return true 正常 false 非法
*/
public boolean checkAllowDownload(String resource) {
return FileUtils.checkAllowDownload(resource);
} public final String getRemotePathWithAuth(String filePath) {
return StringUtils.format("smb://{}:{}@{}{}", SmbShareConfig.getUsername(), SmbShareConfig.getPassword(),
SmbShareConfig.getDomain(), filePath);
} /**
* 获取已连接的文件
*
* @param fullFilePath 全文件地址
* @return
* @throws IOException
*/
protected final SmbFile getSmbFileConnected(String fullFilePath) throws IOException {
// String remotePath = getRemotePathWithAuth(fullFilePath);
// SmbFile file = new SmbFile(remotePath);
// file.connect(); //是否需要连接
// return file; String remotePath = StringUtils.startsWithIgnoreCase(fullFilePath, "smb://") ?
fullFilePath :
StringUtils.format("smb://{}{}{}", SmbShareConfig.getDomain(), fullFilePath.startsWith("/") ? "" : "/", fullFilePath);
NtlmPasswordAuthentication authentication = new NtlmPasswordAuthentication(SmbShareConfig.getDomain(), SmbShareConfig.getUsername(), SmbShareConfig.getPassword());
SmbFile file = new SmbFile(remotePath, authentication);
file.connect(); //是否需要连接
return file;
} /**
* 获取文件地址,并创建文件夹
*
* @param uploadDir
* @param fileName
* @return
* @throws IOException
*/
public final SmbFile getAbsoluteSmbFile(String uploadDir, String fileName) throws IOException {
SmbFile desc = getSmbFileConnected(uploadDir + File.separator + fileName);
desc.connect();
if (!desc.exists()) {
desc.mkdirs();
}
return desc;
} public final SmbFile getAbsoluteSmbFile(String fileName) throws IOException {
SmbFile desc = getSmbFileConnected(fileName);
if (!desc.exists()) {
SmbFile pare = getSmbFileConnected(desc.getParent());
if (!pare.exists()) pare.mkdirs();
}
return desc;
} protected final Session getSmbjSession() {
Session s = null;
try {
SMBClient client = new SMBClient(SmbConfig.createDefaultConfig());
Connection conn = client.connect(SmbShareConfig.getDomain());
s = conn.authenticate(new AuthenticationContext(SmbShareConfig.getUsername(), SmbShareConfig.getPassword().toCharArray(), SmbShareConfig.getDomain()));
} catch (Exception e) {
e.printStackTrace();
}
return s;
} /**
* 将共享文件地址拆分成 共享目录和子目录,smb 2、3使用
*
* @param filePath
* @return
*/
protected final String[] getShareNameAndFileName(String filePath) {
if (filePath == null) return null;
String[] paths = filePath.replace("\\", "/").split("/");
String shareName = "";
List<String> subPaths = new ArrayList<>();
for (String path : paths) {
if (StringUtils.isEmpty(path)) continue;
if (StringUtils.isEmpty(shareName)) {
shareName = path;
continue;
}
subPaths.add(path);
} return new String[]{
shareName,
subPaths.size() == 0 ? "/" : String.join("/", subPaths)
};
} } /**
* 不共享方式实现
*/
class ShareFileUtilsImpl extends ShareFileUtils {
public boolean fileExists(String filePath) throws IOException {
File file = new File(filePath);
return file.exists();
} @Override
public void writeBytes(String filePath, OutputStream os) throws IOException {
FileUtils.writeBytes(filePath, os);
} /**
* 写数据到文件中
*
* @param data 数据
* @return 目标文件
* @throws IOException IO异常
*/
@Override
public String writeImportBytes(byte[] data) throws IOException {
return writeBytes(data, ApplicationConfig.getImportPath());
} @Override
public String writeBytes(byte[] data, String uploadDir) throws IOException {
return FileUtils.writeBytes(data, uploadDir);
} /**
* 删除文件
*
* @param filePath 文件
* @return
*/
public boolean deleteFile(String filePath) {
return FileUtils.deleteFile(filePath);
}
} /**
* smb1共享方式实现
*/
class ShareSmbFileUtilsImpl extends ShareFileUtils { @Override
public boolean fileExists(String filePath) throws IOException {
return getSmbFileConnected(filePath).exists();
} /**
* 读取文件
*
* @param filePath 文件路径
* @param os 输出流
* @throws IOException
*/
@Override
public void writeBytes(String filePath, OutputStream os) throws IOException { SmbFileInputStream fis = null;
try {
SmbFile file = getSmbFileConnected(filePath);
if (!file.exists()) {
throw new FileNotFoundException(filePath);
}
fis = new SmbFileInputStream(file);
byte[] b = new byte[1024];
int length; while ((length = fis.read(b)) > 0) {
os.write(b, 0, length);
}
} catch (IOException e) {
throw e;
} finally {
IOUtils.close(os);
IOUtils.close(fis);
}
} /**
* 写数据到文件中
*
* @param data 数据
* @return 目标文件
* @throws IOException IO异常
*/
@Override
public String writeImportBytes(byte[] data) throws IOException {
return writeBytes(data, ApplicationConfig.getImportPath());
} @Override
public String writeBytes(byte[] data, String uploadDir) throws IOException {
SmbFileOutputStream fos = null;
String pathName = "";
try {
String extension = getFileExtendName(data);
pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
SmbFile file = getAbsoluteSmbFile(uploadDir, pathName);
fos = new SmbFileOutputStream(file);
fos.write(data);
} finally {
IOUtils.close(fos);
}
return FileUploadUtils.getPathFileName(uploadDir, pathName);
} /**
* 删除文件
*
* @param filePath 文件
* @return
*/
@Override
public boolean deleteFile(String filePath) throws IOException {
boolean flag = false;
SmbFile file = getSmbFileConnected(filePath);
// 路径为文件且不为空则进行删除
if (file.isFile() && file.exists()) {
file.delete();
flag = true;
}
return flag;
} }

/**
* smb2|3共享方式实现
*/
class ShareSmbJFileUtilsImpl extends ShareFileUtils {

public ShareSmbJFileUtilsImpl() {

}

private Session session;

private Session getSession() {
boolean isGetNew = false;
if (session == null) {
isGetNew = true;
} else if (session.getConnection() == null) {
isGetNew = true;
} else if (session.getConnection().release()) {
isGetNew = true;
}else if (session.getConnection().isConnected()) {
isGetNew = true;
}
if (isGetNew) session = getSmbjSession();
return session;
}

@Override
public boolean fileExists(String filePath) throws IOException {
String[] filePathArr = getShareNameAndFileName(filePath);
if (filePathArr == null) return false;
try (DiskShare diskShare = (DiskShare) getSession().connectShare(SmbShareConfig.getShareName(filePathArr[0]))) {
return diskShare.fileExists(filePathArr[1]);
}

}

/**
* 读取文件
*
* @param filePath 文件路径
* @param os 输出流
* @throws IOException
*/
@Override
public void writeBytes(String filePath, OutputStream os) throws IOException {

String[] filePathArr = getShareNameAndFileName(filePath);
if (filePathArr == null) return;

InputStream fis = null;
DiskShare diskShare = null;
try {
diskShare = (DiskShare) getSession().connectShare(SmbShareConfig.getShareName(filePathArr[0]));
if (!diskShare.fileExists(filePathArr[1])) {
throw new FileNotFoundException(filePath);
}
if (!diskShare.isConnected())
diskShare = (DiskShare) getSession().connectShare(SmbShareConfig.getShareName(filePathArr[0]));

com.hierynomus.smbj.share.File file = diskShare.openFile(filePathArr[1],
EnumSet.of(AccessMask.GENERIC_READ),
(Set) null,
SMB2ShareAccess.ALL,
SMB2CreateDisposition.FILE_OPEN,
(Set) null
);
fis = file.getInputStream();
byte[] b = new byte[4096];
int length;
while ((length = fis.read(b)) > 0) {
os.write(b, 0, length);
}
} catch (IOException e) {
System.out.println(StringUtils.format("共享错误:{}", JSON.toJSONString(e)));
throw e;
} finally {
IOUtils.close(os);
IOUtils.close(fis);
if (diskShare != null && diskShare.isConnected()) diskShare.close();
}
}

/**
* 写数据到文件中
*
* @param data 数据
* @return 目标文件
* @throws IOException IO异常
*/
@Override
public String writeImportBytes(byte[] data) throws IOException {
return writeBytes(data, ApplicationConfig.getImportPath());
}

/**
* 写数据到文件中
*
* @param data 数据
* @param uploadDir 目标文件
* @return
* @throws IOException
*/
@Override
public String writeBytes(byte[] data, String uploadDir) throws IOException {

String extension = getFileExtendName(data);
String pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
String fullName = uploadDir + "/" + pathName;
writeFile(data, fullName);
return convertToWebUrl(fullName);
//return FileUploadUtils.getPathFileName(uploadDir, pathName);
}

/**
* 删除文件
*
* @param filePath 文件
* @return
*/
@Override
public boolean deleteFile(String filePath) throws IOException {
boolean flag = false;
String[] filePathArr = getShareNameAndFileName(filePath);
if (filePathArr == null) return flag;
try (DiskShare diskShare = (DiskShare) getSession().connectShare(SmbShareConfig.getShareName(filePathArr[0]))) {
if (!diskShare.fileExists(filePathArr[1])) {
return true;
}
diskShare.rm(filePathArr[1]);
flag = true;
} catch (IOException e) {
throw e;
}
return flag;
}

/**
* 写入数据到文件
*
* @param data
* @param filePath
* @throws IOException
*/
private void writeFile(byte[] data, String filePath) throws IOException {
OutputStream fis = null;
try {
String[] filePathArr = getShareNameAndFileName(filePath);
if (filePathArr == null) throw new IOException("未输入文件路径");
DiskShare diskShare = (DiskShare) getSession().connectShare(SmbShareConfig.getShareName(filePathArr[0]));
if (!diskShare.fileExists(filePathArr[1])) {
//创建
String parentPath = filePathArr[1].substring(0, filePathArr[1].lastIndexOf("/"));
if (!diskShare.folderExists(parentPath)) {
diskShare.mkdir(parentPath);
}
}
com.hierynomus.smbj.share.File file = diskShare.openFile(filePathArr[1],
EnumSet.of(AccessMask.GENERIC_ALL),
(Set) null,
SMB2ShareAccess.ALL,
SMB2CreateDisposition.FILE_OPEN,
(Set) null
);
fis = file.getOutputStream();
fis.write(data);
} catch (IOException e) {
throw e;
} finally {
IOUtils.close(fis);
}
}
}

6、关于负载

负载时,共享目录下静态文件处理方案:

  由于资源映射未找到,映射远程共享文件方法,故给出以下方案。

  a、子服务器访问时,请求代理到主服务器资源目录。

  b、添加拦截器,访问到静态资源目录时,通过共享方式获取文件,返回文件数据;项目启动时不做资源映射。

    拦截代码

  

/**
* 在业务处理器处理请求之前被调用
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
ShareFileUtils shareFileUtils = ShareFileUtils.getInstance();
try {
//访问路径为文件地址
String fileName = request.getServletPath();
if (!shareFileUtils.checkAllowDownload(fileName)) {
setErrorMsg(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName), HttpStatus.NOT_FOUND, response, request);
return false;
}
String realFileName = shareFileUtils.getName(fileName);
String filePath = shareFileUtils.convertToDiskPath(fileName);
if (!shareFileUtils.fileExists(filePath)) {
setErrorMsg(StringUtils.format("文件不存在({})。 ", fileName), HttpStatus.NOT_FOUND, response, request);
return false;
}
shareFileUtils.setAttachmentResponseHeader(response, realFileName);
shareFileUtils.writeBytes(filePath, response.getOutputStream()); } catch (Exception e) {
setErrorMsg("文件下载异常", HttpStatus.ERROR, response, request);
return false;
}
//返回文件即不必向下执行
return false;
}

java文件共享实现方案的更多相关文章

  1. 项目四:Java秒杀系统方案优化-高性能高并发实战

    技术栈 前端:Thymeleaf.Bootstrap.JQuery 后端:SpringBoot.JSR303.MyBatis 中间件:RabbitMQ.Redis.Druid 功能模块 分布式会话,商 ...

  2. Java架构师方案—多数据源开发详解及原理(二)(附完整项目代码)

    1. mybatis下数据源开发工作 2. 数据源与DAO的关系原理模型 3. 为什么要配置SqlSessionTemplate类的bean 4. 多数据源应用测试 1. mybatis下数据源开发工 ...

  3. 基于Java IO 序列化方案的memcached-session-manager多memcached节点配置

    在公司项目里想要在前端通过nginx将请求负载均衡,而后台的几组tomcat的session通过memcached(non-sticky模式)进行统一管理,这几组tomcat部署的web app是同一 ...

  4. windows下双击可运行的Java软件打包方案(转)

    出处: http://www.cnblogs.com/shiyangxt/ 刚开始学Java的时候,挺郁闷的,写出来的java类文件,需要dos下编译,然后再dos下运行看效果.这使初学者常常 觉得麻 ...

  5. Java实现统计方案

    统计方案 题目描述 在一无限大的二维平面中,我们做如下假设: 1.每次只能移动一格: 2.不能向后走(假设你的目的地是"向上",那么你可以向左走,可以向右走,也可以向上走,但是不可 ...

  6. 分布式锁1 Java常用技术方案

    前言:       由于在平时的工作中,线上服务器是分布式多台部署的,经常会面临解决分布式场景下数据一致性的问题,那么就要利用分布式锁来解决这些问题.所以自己结合实际工作中的一些经验和网上看到的一些资 ...

  7. java 分布式锁方案

    第一步,自身的业务场景: 在我日常做的项目中,目前涉及了以下这些业务场景: 场景一: 比如分配任务场景.在这个场景中,由于是公司的业务后台系统,主要是用于审核人员的审核工作,并发量并不是很高,而且任务 ...

  8. java编码转化方案-备用

    import java.io.UnsupportedEncodingException; /** * 转换字符串的编码 */ public class changeCharSet { /** 7位AS ...

  9. 分布式锁1 Java常用技术方案(转)

    转:http://www.cnblogs.com/PurpleDream/p/5559352.html#3450419 前言:       由于在平时的工作中,线上服务器是分布式多台部署的,经常会面临 ...

  10. Java秒杀系统方案优化 高性能高并发实战(1)

    首先先把 springboot +thymeleaf 搞起来 ,参考 springboot 官方文档 本次学习 使用 springboot + thymeleaf+mybatis+redis+Rabb ...

随机推荐

  1. IDP中的黄金路径究竟是什么?

    在云原生时代,开发人员面临着越来越多的工具.技术.思维方式的选择,给他们带来了极大的认知负担和工作量.为了提高开发人员的开发效率与开发体验,一些头部科技公司开始建立自己的内部开发者平台(IDP).在之 ...

  2. 企名片Js逆向思路

    企名片Js逆向思路 这个案例不算难,简单说一下思路. 目标链接:https://www.qimingpian.cn/finosda/project/pinvestment 网站更新了https://w ...

  3. nuxt下运行项目时内存溢出(out of memory)的一种情况

    话不多说直接上代码: 如图,点红点的三行引入了一个组件,内容是同意注册协议的弹窗.但是在run dev的时候提示说内存溢出了(out of memory)...经过多方排查,定位到这个组件,警察叔叔就 ...

  4. 我做了一个 VSCode 插件版的 ChatGPT

    大家好,我是风筝 其实很早之前就想学学 VSCode 插件开发了,但是又不知道做什么,加上我这半吊子前端水平,迟迟没有动手. 最近 ChatGPT 火的一塌糊涂,我也一直在用,真的非常好用,有些问题之 ...

  5. 2020-10-29:使用redis实现分布式限流组件,要求高并发场景同一IP一分钟内只能访问100次,超过限制返回异常,写出实现思路或伪代码均可。

    福哥答案2020-10-29: 简单回答:固定窗口:string.key存ip,value存次数.滑动窗口:list.key存ip,value=list,存每次访问的时间. 中级回答:固定窗口:用re ...

  6. 2020-11-28:go中,map的写流程是什么?

    福哥答案2020-11-28: 源码位于runtime/map.go文件中的mapassign函数. info["name"]="福大大" bilibili视频 ...

  7. 2022-01-31:迷宫 III。 由空地和墙组成的迷宫中有一个球。球可以向上(u)下(d)左(l)右(r)四个方向滚动,但在遇到墙壁前不会停止滚动。当球停下时,可以选择下一个方向。迷宫中还有一个洞

    2022-01-31:迷宫 III. 由空地和墙组成的迷宫中有一个球.球可以向上(u)下(d)左(l)右(r)四个方向滚动,但在遇到墙壁前不会停止滚动.当球停下时,可以选择下一个方向.迷宫中还有一个洞 ...

  8. vue全家桶进阶之路14:常用属性和方法

    Vue2中常用的属性和方法: 属性 el:用于指定Vue实例挂载的元素,可以是CSS选择器.HTML元素或Vue组件. data:用于存储Vue实例的响应式数据,也可以是一个函数,返回一个对象,用于提 ...

  9. 一些JS过滤方法

    一般过滤器我们都会卸载过滤filter文件内 本文这里就直接写正常methods格式的 //过滤空格 filterSpaces(data) { return data.replace(/\s+/g, ...

  10. Element Cascader 级联选择器去除空叶子节点

    此处以后端获取部门级联List为例 以下为数据结构 { data: { children: [ 0:{childre:[ 0:{}, 1:{} ]}, 1:{}, 2:{}, 3:{}, 4:{}, ...