| ページ一覧 | ブログ | twitter |  書式 | 書式(表) |

MyMemoWiki

Java Windowsファイル共有サービス(CIFS)経由でファイルを書き込む

提供: MyMemoWiki
ナビゲーションに移動 検索に移動

Java Windowsファイル共有サービス(CIFS)経由でファイルを書き込む

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;
        }
    }
}

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<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);
        }
    }
}