Javaでファイルをコピーするプログラムをbatファイルから動かして毎日やる作業を少しだけ効率化する話


まえがき

社内日誌を毎日提出することになりました。
毎日ファイルをコピーして、今日の日付を入れて、とするのは正直大変。クリック下手だし。

というわけでできるだけ自動化しました。

具体的にはダブルクリック1回でファイル名に日付が入ったコピーが生成されるように。
xlsファイルの中身は弄ってません。それができるライブラリもあるらしいけど、今回は見送り。

使ったのはJavaとbatchファイル。

やりたかったことは一通りできて勉強になったのでよし。

以下説明。

Java

とりあえずプログラム本体から。

プログラム

copyReport.java
package copyReport;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

 public class CopyReport {
    public static void main(String[] args) {
        //デスクトップにフォルダを置いてもらうようにする
        String desktopPath = "C:\\Users\\"+System.getProperty("user.name")+"\\Desktop";
        String folderPath = desktopPath+"\\copyreport";

        //プログラムフォルダ内のファイル名を配列で取得
        File[] files = new File(folderPath).listFiles();

        //コピー元ファイル名を取得。自分の名前に変えられるようにした。
        String baseFileName = "";
        for(File f:files) {
            if(f.getName().matches(".*日報_.*\\.(xls|xlsx|ods)")) {
                baseFileName = f.getName();
            }
        }

        //コピー元ファイルの有無をチェック
        if(baseFileName.isEmpty()) {
            System.out.println("copyreport下にコピー元ファイルが見つかりませんでした。");
            return;
        }
        File baseFile = new File(folderPath,baseFileName);
        if(!baseFile.exists()) {
            System.out.println("コピー元ファイルが見つかりませんでした。");
            return;
        }

        //日付から今日のぶんのファイル名を生成
        String today = LocalDate.now().format(DateTimeFormatter.ofPattern("YYYYMMdd"));
        String outFileName = today + baseFileName.substring(baseFileName.lastIndexOf("日報_"));
        System.out.println(outFileName + "としてコピーします。");

        //既にあるかチェックする
        File outFile = new File(desktopPath,outFileName);
        if(outFile.exists()) {
            System.out.println("コピー先ファイルがすでに存在します。");
            return;
        }

        //メッセージを出力
        System.out.println("ファイルをコピーします。");
        System.out.println("コピー元フォルダ名;"+folderPath);
        System.out.println("コピー元ファイル名:"+baseFile.getName());
        System.out.println();
        System.out.println("コピー先フォルダ名;"+desktopPath);
        System.out.println("コピー先ファイル名:"+outFile.getName());

        try {
            //コピー元・コピー先のFileインスタンスからstreamを作成
            FileInputStream fis = new FileInputStream(baseFile);
            FileOutputStream fos = new FileOutputStream(outFile);

            //バッファと読み込みバイト数を管理
            byte[] buf = new byte[256];
            int len=0;
            //ファイルの終わりまでbufずつstreamを読み込んで書き出す
            while((len = fis.read(buf)) != -1) {
                fos.write(buf,0,len);
            }

            //書きそびれの部分を書き出す
            fos.flush();

            //ファイルへのアクセスを終わる
            fos.close();
            fis.close();

            //メッセージ
            System.out.println("ファイルのコピーに成功しました。");

        } catch (FileNotFoundException e) {
            System.out.println("ファイルのコピーに失敗しました。");
            e.printStackTrace();
        } catch (IOException e) {
            System.out.println("ファイルのコピーに失敗しました。");
            e.printStackTrace();
        }

    }

}

プログラムの説明

広く公開するツールではないので、「デスクトップに直接フォルダを置いて実行させる」つもりで組んであります。

デスクトップの場所

使ってるWindows10のデスクトップの場所はC:\user\(USER名)\Desktopで人によって違うので、それをSystem.getproperty(username)でもらってきて入れる。

File配列

プログラム、コピー元ファイル、実行用batファイルを入れるためのフォルダなので、全体の名前を取得→コピー元ファイルの名前を取得の2段構成。

match条件

(任意の日付項目)日報_(任意の名前).(xlsかxlsxかods)
の正規表現。これに一致したファイルをコピー元ファイル名として取得する。
こうすることで、元のファイル名に自分の名前が入っていればそのままコピーしてくれるようになった。

コピー元の有無チェック

過剰な気がしないでもないが、抜けがあったときのほうが面倒なため。

日付からファイル名を生成

今日の日付を8桁の数字列として取得する。
それをコピー元ファイル名の前半に入れる形でコピー先のファイル名を作る。

コピー先チェック

一度内容を書いた日報が上書きされたら困る(そもそも上書きしなかった気がするけど)ので一応チェック。

ファイルのコピー

今回はFileInputStreamとFileOutoutStreamを使うことにした。
FileInputStreamからlenバイトずつ読み込んだ中身であるbufをFileOutputStreamに流して~、ということらしい。調べたらどの資料にも同じように書いてあったけれど、そのせいでサンプルが少なく仕組みが理解しづらくなっていた。

finally処理

流し切らなかった部分を手動で流したりアクセス終了しておしまい。
Java8以降だとアクセス終了処理とかはやってくれるらしい。

 

というわけでjavaのプログラミングはここまで。あとはEclipseからエクスポートしたりjarをダブルクリックで実行できるようにしたり。

Eclipseでのエクスポート

ファイル→エクスポート→実行可能jarファイル、で流れるままにやる。jarファイル名とプロジェクト名とかメインクラス名とかってどこまで揃えるべきなんでしたっけね。
デスクトップあたりにjarファイルが生成していれば完了。

Java実行環境の構築

JREと呼ばれるやつ。Java Runtime Environmentだったかな。
コマンドプロンプトからjavaを実行(javaコマンド、javacコマンド)するのに必要。

Eclipseがあるなら話が早くて、画面→設定→Java→実行環境からパスを持ってきて、それをPCの環境変数に入れる。
直接java.exeやjavac.exeを指定してもいいらしいけど、今回はそれらが直下にあるフォルダを指定。
一度入れたら入れっぱなしにするものなので、入れることをすっかり忘れていて焦った。

実行の確認

コマンドプロンプトで.jarファイルのあるフォルダから「java -jar copyReport.jar」を動かして動作確認。ファイル名はEclipseからエクスポートした時のもの。

batファイルを作る

適当な名前をつけた.batファイルを作ってメモ帳で編集。
中身こんな感じ

copy.bat
@echo off
cd ./copyreport
java -jar copyReport.jar
timeout 5 

説明

1行目

以降のコマンド入力内容は映さなくていいよ(echo off) の命令を映さなくていいよ(@)

2行目

コンソールの現在位置を起動したフォルダの直下にあるcopyreportフォルダに移動するコマンド。
失敗しても落ちたりしないので安心して実行できる。copyreportフォルダ内にcopyreportって名前のフォルダ失敗するとこの先が動かなくなるってデメリットがあるけど。

3行目

指定したjava系のファイルをjarとしてjavaで実行してくれ

4行目

5秒後にこの画面を閉じるように。
メッセージが出るけど消したい場合は後ろに 「> nul」をつけるらしい。
これの意味は「メッセージの出力先はnulにしてくれ」だそうな。

学習したこと

ファイル操作の復習がてら書いたけどほぼ丸一日かかってしまった。
FileInputStream#readが複雑で、オーバーロードするメソッドしだいでbufとlenの内容がコロコロ化けるらしく、正直とても困った。
実行環境まわりのことはコロコロPCを変えるわけでもないぶん余計に忘れがちなので意識的に気を付けておきたい。