[Java][JAX-RS]
- アプリケーションサーバー等、サービスで動くアプリケーションは、ログインユーザーとは別権限で動作しており、ファイル共有されたドライブのファイル参照、書き出しが出来ない。
- JCIFSライブラリを利用して対応する
package info.typea.util;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.MalformedURLException;
import jcifs.smb.SmbException;
import jcifs.smb.SmbFile;
import jcifs.smb.SmbFileOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ファイルシステムユーティリティ
*
*/
public class FileSystemUtils {
protected static Logger logger = LoggerFactory.getLogger(FileSystemUtils.class);
private static final int BUF_SIZE = 4096;
private FileSystemUtils(){}
/**
* 通常のファイルシステムもしくは CIFS(Windowsファイル共有サービス) にファイルを書き込む
*
* @param is 書き込み内容
* @param file 書き込み対象ファイル
* @param isCifs CIFS か否か
* @return
*/
public static FileSystemUtilInfo writeToFile(
InputStream is,
File file,
boolean isCifs,
String user,
String password) {
if (isCifs) {
return writeToCifsFile(is, file, user, password);
} else {
return writeToLocalFile(is, file);
}
}
/**
* java.io.File から、SmbFile オブジェクトを生成する
*
* @param targetFile
* @param user
* @param password
* @return
*/
public static SmbFile createSmbFileFromFile(
File targetFile,
String user,
String password) {
SmbFile file = null;
try {
file = new SmbFile(
cifsUrl(
user,
cisfPassword(password),
cisfPath(targetFile.getAbsolutePath())));
} catch (MalformedURLException e) {
// TODO
}
return file;
}
/**
* 通常のファイルシステムにファイルを作成
*
* @param is
* @param targetFile
* @return
*/
private static FileSystemUtilInfo writeToLocalFile(InputStream is, File targetFile) {
FileSystemUtilInfo info = new FileSystemUtilInfo();
FileOutputStream fos = null;
try {
fos = new FileOutputStream(targetFile);
BufferedInputStream bis = new BufferedInputStream(is);
int len = 0;
long fileSize = 0;
byte[] buffer = new byte[BUF_SIZE];
while ((len = bis.read(buffer)) >= 0) {
fos.write(buffer,0, len);
fileSize += len;
}
info.setFileSize(fileSize);
bis.close();
} catch(Exception e) {
throw new IllegalStateException(e);
} finally {
try {
fos.close();
} catch (Exception e2) {}
}
return info;
}
/**
* CIFS ファイルシステムにファイルを作成
*
* @param is
* @param targetFile
* @return
* @see http://jcifs.samba.org/src/docs/api/jcifs/smb/SmbFile.html
*/
private static FileSystemUtilInfo writeToCifsFile(
InputStream is,
File targetFile,
String user,
String password) {
FileSystemUtilInfo info = new FileSystemUtilInfo();
try {
makeCifsDirectories(targetFile.getParentFile(), user, password);
SmbFile file = new SmbFile(
cifsUrl(
user,
cisfPassword(password),
cisfPath(targetFile.getAbsolutePath())));
if (!file.exists()) {
file.createNewFile();
}
SmbFileOutputStream fos = new SmbFileOutputStream(file);
BufferedInputStream bis = new BufferedInputStream(is);
int len = 0;
long fileSize = 0;
byte[] buffer = new byte[BUF_SIZE];
while ((len = bis.read(buffer)) >= 0) {
fos.write(buffer,0, len);
fileSize += len;
}
info.setFileSize(fileSize);
bis.close();
} catch (Exception e) {
// TODO
}
return info;
}
/**
* CIFS ディレクトリが存在しない場合、作成する
*
* @param dir
* @param user
* @param password
* @throws MalformedURLException
* @throws SmbException
*/
private static void makeCifsDirectories(
File dir,
String user,
String password) throws MalformedURLException, SmbException {
SmbFile cifsDir = new SmbFile(
cifsUrl(
user,
cisfPassword(password),
cisfPath(dir.getAbsolutePath())));
if (!cifsDir.exists()) {
cifsDir.mkdirs();
}
}
/**
* CIFS アクセス URL
* @param user
* @param password
* @param path
* @return
*/
private static String cifsUrl(String user, String password, String path) {
return String.format("smb://%s:%s@%s",user,password,path);
}
/**
* '@' is URL encoded with the '%40' hexcode escape
*
* @param password
* @return
*/
private static String cisfPassword(String password) {
return password.replaceAll("@", "%40");
}
/**
* "\\" で始まる、UNCパス を "server/share/path/to/file.txt" のような書式に変換
*
* @param path
* @return
*/
private static String cisfPath(String path) {
// \\ を取り除く
if (path.substring(0,2).equals("\\\\")) {
path = path.substring(2);
}
return path.replaceAll("\\\\", "\\/");
}
/**
* 結果情報
*
*/
public static class FileSystemUtilInfo {
private long fileSize;
public long getFileSize() {
return fileSize;
}
public void setFileSize(long fileSize) {
this.fileSize = fileSize;
}
}
}
- SmbFile を JAX-RS のレスポンスとして返すためのプロバイダ
package info.typea.provider;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import jcifs.smb.SmbException;
import jcifs.smb.SmbFile;
import jcifs.smb.SmbFileInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Windows ファイル共有 サービス(CIFS) ファイル 用 JAX-RS プロバイダー
*
* @see org.apache.wink.common.internal.providers.entity.FileProvider
*/
@Provider
@Produces({ "*/*" })
@Consumes({ "*/*" })
public class SmbFileProvider implements MessageBodyWriter<SmbFile> {
private static final Logger logger = LoggerFactory.getLogger(SmbFileProvider.class);
@Override
public long getSize(SmbFile t, Class<?> type, Type genericType, Annotation[] annotations,
MediaType mediaType) {
try {
return t.length();
} catch (SmbException e) {
return 0;
}
}
@Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations,
MediaType mediaType) {
return SmbFile.class.isAssignableFrom(type);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public void writeTo(
SmbFile t,
Class<?> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream) throws IOException, WebApplicationException {
try {
final SmbFile smbFile = t;
final OutputStream os = entityStream;
AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws IOException {
if ((!(smbFile.canRead())) || (smbFile.isDirectory())) {
if (SmbFileProvider.logger.isWarnEnabled()) {
SmbFileProvider.logger.error(String
.format("cannotUseFileAsResponse %s", smbFile.getPath()));
}
throw new WebApplicationException();
}
SmbFileInputStream fis = new SmbFileInputStream(smbFile);
try {
SmbFileProvider.this.pipe(fis, os);
} finally {
fis.close();
}
return null;
}
});
} catch (PrivilegedActionException e) {
if (e.getException() instanceof IOException)
throw ((IOException) e.getException());
if (e.getException() instanceof WebApplicationException)
throw ((WebApplicationException) e.getException());
throw new WebApplicationException(e.getException());
}
}
private void pipe(InputStream is, OutputStream os) throws IOException {
byte[] ba = new byte[1024];
int i = is.read(ba);
while (i != -1) {
os.write(ba, 0, i);
i = is.read(ba);
}
}
}
YAGI Hiroto (piroto@a-net.email.ne.jp)
twitter http://twitter.com/pppiroto
Copyright© 矢木 浩人 All Rights Reserved.