!!!Java Windowsファイル共有サービス(CIFS)経由でファイルを書き込む [Java][JAX-RS] *アプリケーションサーバー等、サービスで動くアプリケーションは、ログインユーザーとは別権限で動作しており、ファイル共有されたドライブのファイル参照、書き出しが出来ない。 *JCIFSライブラリを利用して対応する **http://jcifs.samba.org/ !!書き込みユーティリティ例 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; } } } !!JAX-RS のプロバイダ例 *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 { 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 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); } } }