• ベストアンサー

windows 環境でのファイル操作について

今作成しているjavaのアプリケーションがあるのですが、 うまく動きません。 ファイルサーバにアップロードされるファイルを数秒間ごとに 監視し、アップロードされれば自動で別のディレクトリにコピ ーするというアプリケーションを作成しております。 で、MAC osX server上で動かすと正常に動くのですが、 Windows 2000 server上だと正常に動きません。 プログラムの流れとしてはFile型のorigオブジェクト(元ファイル) と、File型のcopyオブジェクト(移動先のファイル)を用意し、 BufferedInputSteramで読み取って、BufferedOutputStreamで 書き出す、というものです。 うまく動かない箇所は、origファイルがまだアップロード中で ある場合そのファイルに対してFileInputStreamを用意しようと すると、FileNotFoundExceptionが発生するだけでなく、その ファイルが消去されてしまうのです。恐らくはJavaではなく、 OSがコピーエラーが発生したかのように扱ってしまい、ファイル を消去してしまうのではないかと思っているのですが。(osXでは 動くので) FileInputStream fis = new FileInputStream(orig); この行で例外発生!ファイルが消える。 そこでFileInputStreamを用意する前に、そのファイルがコピー中 であるのか、コピーが終わっているのかを調べることができるの でしょうか、というのが私の質問です。あるいは、全く違った側面 からの回避策でも結構です。 ちなみにorig.exists()はアップロード中であってもtrueが返され ます。 javaはプラットホームに依存しないと信じてたのに大きな落とし穴 でした。以下にソースの一部を記述しておきますので、よろしく おねがいします。

  • Java
  • 回答数6
  • ありがとう数4

質問者が選んだベストアンサー

  • ベストアンサー
  • sasadora
  • ベストアンサー率68% (59/86)
回答No.5

気になったんでちょっと試してみました。 環境は違うんですが、おそらく同じような結果が得られました。 環境:WindowsXP JVM:JDK1.3.1_02 コピー中のファイルに対しては確かにexistsがtrueになりました。 InputStreamを取得すると、 java.io.FileNotFoundException: test.txt (プロセスはファイルにアクセスできません。別のプロセスが使用中です。) という例外がthrowされました。 ここまで同じような結果が得られるのですが、 私が試した結果ではファイルは消えません。そのままコピーが継続されました。 例外が発生したときに何かしていますか? ファイルが消えてしまうのは何か別の原因ではないでしょうか?

saguaro
質問者

お礼

お騒がせいたしました。結局XPでは試せなかったのですが、FileInputStreamを取得せずとも、FileクラスのrenameTo()メソドでファイルパスを変えてやることにより、ファイルの移動が可能なことが解りました。(これって常識?) このメソドでは、色々な状況でテストしてみましたが、コピー中のファイルに対して行っても、ファイルが消える、壊れる、といった現象は起きません。 もっともこの場合ファイルのコピーではなく移動なのですが、今回やりたかったことはとりあえずこれで実現できました。 いろいろ試していただいたようなので、報告を兼ねてお礼いたします。では。

saguaro
質問者

補足

貴重な情報ありがとうございます。すぐにXPを用意できないので試してないのですが、他を色々試すうちに新しいことがわかりました。それはファイルをコピーしているクライアントのOSがmacOSだとファイルが消去され、クライアントがwindowsだとおっしゃる通り、FileNotFoundExceptionの後、正常にコピーされるということです。つまりなんらかの形でOS間の互換性の問題があるということでしょうか。 原因がjavaとは関係の無いところにあるのでしょうけど、クライアントをwin系のOSに限定することは出来ないのです。なんとかjava側で回避策が作れないものでしょうか? ちなみに、例外をcatchするブロックではデバグの為にprintStackTrace()を行っている以外何もやっておりません。さらにプログラムのその後の処理を削除しても結果は同じなので他にjavaのプログラムがファイルを消去するようなことは考えられません。

その他の回答 (5)

  • COOKY2
  • ベストアンサー率28% (2/7)
回答No.6

問題のフォルダのオブジェクト.lists() で、ファイルやサブフォルダがString[]が返りますよね。 これで確認してからでは、いかがでしょうか?

saguaro
質問者

補足

コピー中のファイルもFile型のオブジェクトとして認識します。

  • a-kuma
  • ベストアンサー率50% (1122/2211)
回答No.4

実は JVM の障害で、バージョンを上げたり/下げたりすると動く、ってことは… 後は、ファイルの時刻をコピー開始の時刻とみなして、適当な時間が経ってからコピーかなあ。

saguaro
質問者

お礼

いろいろお騒がせいたしましたが、結局FileInputStreamを使わず、renameTo()メソドを試してみたらファイルの移動が問題なく実現いたしました。 問題が全て解決したわけではありませんが、一応自分のやりたかったことは実現できました。ありがとうございました。

saguaro
質問者

補足

そうですね。原因がJVMの方にあるということも考えられなくはないです。 ですが、osXでもwin2000でも同じsdk1.4.1で試して、動きが異なったことからも、原因はos環境ではないかとまず疑ったのです。さらに新しくわかったのですが、win2000環境でもファイルをアップロードするクライアントが同じwin2000の端末からだと、ファイルが消える現象は起きません。やはり問題はos環境と考えて良いのではと思っています。 また、ファイルの更新時刻で判断する方法ですが、クライアント数が多く頻繁に使われるサーバー上でのプログラムなので、厳密で無い処理は避けたいと思っています。 ともかく、意見交換ができて非常にためになります。ありがとうございます。

  • sasadora
  • ベストアンサー率68% (59/86)
回答No.3

exists()がtrueを返すのに、InputStreamを作ろうとすると、 FileNotFoundExceptionを返すんですか。 それは難しいですね。 試していないんで分からないんですが、 アップロード中のファイルに対して、 isFile()とか、canRead()とか、length()とかを チェックしてみて、アップロードが完了したファイルと、 相違点があれば、それを基準にできるかも。 あとは、一定時間サイズが増加しなかったら、 アップロードしたとみなすとか。でも、これだと完全じゃないかも 知れませんね。

saguaro
質問者

補足

ヤケになっていろいろ試しましたが、 コピー中のオブジェクトに対して canRead() : true canWrite() : true delete() : true isFile() : true length() : 完全なファイルサイズ(コピー後と同じ) となります。無理っぽいですね。

  • a-kuma
  • ベストアンサー率50% (1122/2211)
回答No.2

確実に問題を回避できる保証はありませんが、サイズのチェックをしたらどうでしょう。 まず、ファイルを見つけたら、ファイルパスとサイズを保持しておいて、次の監視タイミングで 前回(*)のサイズと同じであれば、コピーが完了しているものとして、処理を行う。   (*) 回線の重さ次第では、監視間隔以内にコピーがされていない可能性もあるので、     二回後、とか、三回後とか調整してみる # ま、こういった処理は General には書けない、ってことでしょうね

saguaro
質問者

補足

回答ありがとうございます。 実はサイズのチェックも試してみたんですが、コピーが始まった時点で完全なファイルとしてのサイズが記録されるようで、コピーしながらファイルサイズが大きくなるというものでは無いようです。(これもOS依存かもしれませんが) 結局ファイルのコピーという部分をOS任せにしている以上、難しいのですね。 それにしても「javaでの例外の発生」-->「os側でのファイルの消去」というwindowsのふるまいは納得いきません。 裏技のようなテクニックでもないものかと。。。

  • sasadora
  • ベストアンサー率68% (59/86)
回答No.1

いろんな方法があると思いますが、 監視プログラムの方の修正ではなく、 アップロードプログラム(Servlet?)の方の修正になってしまいますが、 アップロードするファイルが作成中の間は、テンポラリなファイルに 書き込んで、アップロード完了後はリネームするような動作にしたら どうでしょうか? 例 アップロード中:file_123456.tmpに書き込み  ↓ アップロード完:file_123456.txtにリネーム 監視プログラムは、*.tmpファイルは対象外にして、 アップロードが完了したファイル(*.txt)だけを監視する。

saguaro
質問者

補足

早速の回答ありがとうございます。 アップロードと書きましたが、それは単なるイントラ内でのファイルのコピーです。従ってそこにはアプリケーションは存在しません。つまりどうしても監視側でコピー完了を確認しなければならないんです。 いかがでしょうか?

関連するQ&A

  • socketでのバイナリファイルの扱い方

    javaのsocketを用いてファイルの送信サーバ、受信クライアントを作成しているのですが テストプログラムとしてスレッド化せずに送信・受信部のみ作りました。 症状はバイナリデータの転送がうまくできないことです。ソースを抜粋すると サーバ側で BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream());//socketはSocketクラス BufferedInputStream in1 = new BufferedInputStream( new FileInputStream("test1.bmp")); BufferedInputStream in2 = new BufferedInputStream( new FileInputStream("test2.bmp")); while( (c = in1.read() ) != -1 ){   out.write(c); } while( (c = in2.read() ) != -1 ){   out.write(c); } クライアント側で BufferedOutputStream out1 = new BufferedOutputStream( new FileOutputStream("test1.out.bmp") ); BufferedOutputStream out2 = new BufferedOutputStream( new FileOutputStream("test2.out.bmp") ); BufferedInputStream in = new BufferedInputStream( s.getInputStream() ); while( ( c=in.read() ) != -1 ){   out1.write(c); } while( ( c=in.read() ) != -1 ){   out2.write(c); } とやっています。 クライアント側で1回目のin.readを抜けるときにはサーバ側は既に2回のwriteを終わっているようで ファイルはtest1.out.bmpにマージされていました。 サーバ側の1回目のwriteのオブジェクトがcloseしていないことが原因だと思い1回目のwriteが終わった時点でout.close()とした のですがそうやるとsocketも閉じてしまいsocket closeのエラーが生じてしまいました。 このようにならず1本のコネクションでバイナリのデータを複数送信する方法をご存知でしたら教えてください。 [環境] win2ksp1a,j2sdk1.4.1_03,java経験4ヶ月程度

  • Javaでのファイルアップ、ダウンロードについて

    Javaでのファイル(画像やテキストなど)の アップロードとダウンロードの仕組みについて、質問をさせていただきます。 上記を実現するにはストリームを利用することで実現可能であることを知りました。Javaの道具?としてinputstreamとoutputstreamがあります。 ストリームの仕組みについて、教えていただけないでしょうか。 以下は私の想定です。 ◇アップロードの場合 端末上でアップロードしたいファイルを用意すると、inputstreamがファイルをプログラム上(メモリ?)にデータをコピーします。そのあとoutputsreamでプログラム上にコピーしたデータをサーバー上にコピーする。 ◇ダウンロードの場合 サーバー上のファイルをinputstreamでプログラム上(メモリ?)にコピーして、outputstreamでプログラム上にコピーされたデータをローカルにコピーする。

  • java XMLファイル 操作

    現在javaにてXMLファイルにデータベースの接続情報を保存する処理を 作成しています。XMLファイルの読み書きは問題なく動作するのですが、テストの為にXMLファイルを更新(データベースの接続情報は誤りの状態)にしました。その後データベースに接続して行う処理を行う際にはエラーとなる事を期待しているのですが、正常に処理されてしまいます。XMLファイルを更新した時点でテキストエディタ等で確認すると確かに更新されています。 なぜデータベースに接続して行う処理が正常に流れるのか疑問です。 ただし、いったんシステムの実行を終了して、再度実行すると、期待通りエラーとなります。 何か分かる方がいらっしゃればと思い質問いたしました。 <XML読込> public Object readXML(){ Object objBean = null; try{ // XMLからオブジェクトへの変換オブジェクトを生成する。 XMLDecoder xmlDecoder = new XMLDecoder( new BufferedInputStream( new FileInputStream(this._strFile))); // 指定したXMLファイルからオブジェクトにデータを読み込む objBean = xmlDecoder.readObject(); // 変換オブジェクトを閉じる xmlDecoder.close(); }catch(FileNotFoundException e){ // 指定されたパス名で示されるファイルが開けなかった場合の例外 bs.Setlog("readXML","FileNotFoundException",e.getMessage()); } <XML書込> public Boolean writeXML(Object objBean){ try{ // オブジェクトからXMLへの変換オブジェクトを生成する XMLEncoder xmlEncoder = new XMLEncoder( new BufferedOutputStream( new FileOutputStream(this._strFile))); // 指定したXMLファイルにオブジェクトデータを書き込む xmlEncoder.writeObject(objBean); // 変換オブジェクトを閉じる xmlEncoder.close(); // XMLファイル書込成功 return true; }catch(FileNotFoundException e) { // 指定されたパス名で示されるファイルが開けなかった場合の例外 bs.Setlog("writeXML","FileNotFoundException",e.getMessage()); return false; // 書込失敗 } }

    • ベストアンサー
    • Java
  • バイナリファイルを転送したいのですが

    お世話になります socketを用いてサーバへファイルを転送したいのですが、うまくいきません。 以下ソースからの抜粋です BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream()); BufferedInputStream in = new BufferedInputStream(new FileInputStream("test.txt")); int c; while((c = in.read()) != -1 ){  out.write(c); } 上記のようなコーディングでは転送できないのでしょうか? 出来ればファイルから1024バイトずつ読み込んで1024バイトずつ出力していき、ファイルの終端まで繰り返す処理をしたいのですが、よい方法をご存知でしたら教えていただけないでしょうか?

    • ベストアンサー
    • Java
  • 固定長データのbyteスキップについて

    バイナリ入出力でご質問がございます。 下記の例のように各レコードにある 先頭5byte付与されたデータを スキップ(破棄)して 各レコード10byteずつ読み込みたいのですが なにかサンプルデータもしくは アドバイス頂けないでしょうか? どうぞよろしくお願い致します。 例 1レコード 10byte (先頭5byte付与) LLLLL1234567890 LLLLL1234567890 LLLLL1234567890 LLLLL1234567890 LLLLL1234567890 ↓ 1234567890 1234567890 1234567890 1234567890 1234567890 import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.nio.ByteBuffer; import java.io.ByteArrayOutputStream; public class dat{ public static void main(String[] args) { String inputFileName = ""; String outputFileName = ""; // ファイルオブジェクトの生成 File inputFile = new File(inputFileName); File outputFile = new File(outputFileName); try { FileInputStream fis = new FileInputStream(inputFile); BufferedInputStream bis = new BufferedInputStream(fis); FileOutputStream fos = new FileOutputStream(outputFile); BufferedOutputStream bos = new BufferedOutputStream(fos); byte[] buf = new byte[17]; int len = 0; while ((len = bis.read(buf, 0, 17)) == 17) { bos.write(buf, 0, 17); } bos.flush(); bos.close(); bis.close(); } catch(Exception e) { e.printStackTrace(); } }

    • ベストアンサー
    • Java
  • レコード長からのbyteスキップ設定について

    QNo.8229324から あらたに新規でご質問させて頂きます。 1レコードごとに10byteあったとします。 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 レコード長からスキップ設定を35byteと設定したとします。 40byteごとに頭の付与された5byteをスキップさせたいのですが なにかサンプル及びアドバイス頂けますでしょうか? 仕様案 (1)まずはレコード先端の5byteをスキップ (2)次は35byteを超えた時点で5byteスキップを繰り返し。 どうぞ宜しくお願い致します。 import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.nio.ByteBuffer; import java.io.ByteArrayOutputStream; public class dat{ public static void main(String[] args) { String inputFileName = ""; String outputFileName = ""; // ファイルオブジェクトの生成 File inputFile = new File(inputFileName); File outputFile = new File(outputFileName); try { FileInputStream fis = new FileInputStream(inputFile); BufferedInputStream bis = new BufferedInputStream(fis); FileOutputStream fos = new FileOutputStream(outputFile); BufferedOutputStream bos = new BufferedOutputStream(fos); byte[] buf = new byte[17]; int len = 0; while ((len = bis.read(buf, 0, 17)) == 17) { bos.write(buf, 0, 17); } bos.flush(); bos.close(); bis.close(); } catch(Exception e) { e.printStackTrace(); } }

    • ベストアンサー
    • Java
  • サーバへファイルコピーについて

    JSPとServletでクライアントPCにあるファイル(Excel、PDF等)をサーバにコピーするプログラムを作っています。 アプリケーションサーバ:SERVER1 ファイルサーバ    :SERVER2 最終的には上記のようにしたいのですが、今はアプリケーションサーバ、ファイルサーバともSERVER1で開発しております。 下記のようなコーディングして、クライアントPCにあるファイル(FILE1)をSERVER1のフォルダにコピーしようとすると「(FILE1の)ファイルパスが見つかりません。」というエラーが出てしまいます。SERVER1にあるファイルをSERVER1にある別のフォルダにコピーすることはできます。 クライアントPCのファイルをサーバへコピーするには何か設定が必要でしょうか? それともコーディングが悪いのでしょうか? ご存知の方、お知恵を貸してください。 宜しくお願い致します。 ============================================= public void copy(String src, String dest) throws IOException { FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream(src); out = new FileOutputStream(dest); FileChannel cIn = in.getChannel(); FileChannel cOut = out.getChannel(); cIn.transferTo(0, cIn.size(), cOut); } finally { try { if(in != null) { in.close(); } } catch(IOException e) { } try { if(out != null) { out.close(); } } catch(IOException e) { } } } 以上、宜しくお願い致します。

    • ベストアンサー
    • Java
  • サーバの冗長化によるWeb上からのファイルアップロード処理について

    お世話になります。 仕事でサーバを冗長化するという話がでているのですが、 サーバを冗長化し、同一の内容のサーバを複数用意した際に Web上からのファイルアップロードしたファイルの扱いにどうするかで迷っております。 現在、案として3点でているのですが、 1点目は、アップロードしたファイルをバイナリ化しDBに登録する方法。 2点目は、ファイルをアップロードした瞬間に、rsyncを実行し、別サーバに同一のファイルをコピーする方法。 3点目は、共有ディスクを用意し、そこにファイルを保存する方法。 どの手法も一長一短があり、どれにするべきか決めかねてる状態であります。 それともこの手法以外にも別の手法があるのでしょうか? また、一般的には冗長化した際のWeb上からのファイルアップロード処理はどのように行っているのでしょうか? 大手のサイトは、付加分散するために、冗長化してると思うのですが、アップロードファイルをどのように扱っているのかわかりません。 どなたか、ご存知の方がいらっしゃるようであれば、教えていただけないでしょうか? どうかよろしくお願い致します。

  • Windows Server 2008のWindows Server バックアップ時のアプリケーションログファイルについて

    Windows Server 2008のWindows Server バックアップ(1回限り)のウィザード中の「詳細オプション」という項目で、以下の二つのどちらかを選択する必要があります。 1. VSS コピー バックアップ(推奨) 2. VSS 完全バックアップ 「VSS 完全バックアップ」の説明にある、 「各ファイルのバックアップ履歴が更新され、アプリケーションログファイルの内容が消去されます。」 について以下の3点が疑問点です。 A. 「各ファイルのバックアップ履歴が更新され、」とは具体的にどういったことでしょうか? B. 「アプリケーションログファイル」とは具体的に何を指しているのでしょうか? C. アプリケーションログファイルが削除されるのは、バックアップ元でしょうか?それともバックアップ先でしょうか? ご回答のほど、よろしくお願いいたします。

  • ファイルのコピー

    あるサーバーからあるサーバーにファイルをPHP上でコピーしたいのですが、 PHPでコピーするというのはアップロードとなるのでしょうか?? 以前は画像のファイルをアップロードしたのですが、 それは_FILEを受け取っていました。 今度のファイルは固定の場所にあるので、そのファイルをわざわざ参照するわけではありません。 現在あるファイル場所を指定して、うまくアップロードすることはできるのでしょうか? パソコン側で受け取るときにディレクトリ名が普通のパスとは違った表示で受け取っているように見えるのすが。

    • ベストアンサー
    • PHP