【Java】フォルダ圧縮処理

フォルダ内のファイルを圧縮します。

/** 
 * フォルダ圧縮処理
 * @param 圧縮対象フォルダ「C:\work\dir」
 * @param 圧縮ファイル名「C:\work\send_20181104150000.zip」
 */
private void dirCompress(String dir, String zipFileName) throws Exception {

  // 圧縮ストリームオブジェクトを宣言する。
  ZipOutputStream zos = null;

  try {
    // 圧縮ストリームオブジェクトを生成する。
    zos = ZipOutputStream(
      new FileOutputStream(
        new File(zipFileName)), Charset.forName("MS932"));

    // フォルダ内のファイル一覧を取得する。
    List<Path> filePahtList = Files.list(Paths.get(dir).collect(Collectors.toList());

    // ファイル一覧分繰り返す。
    for (Path filePath : filePathList) {
      archive(zos, filePath, Paths.get(dir).getFileName().toString() + "\\" + filePath.getFileName().toString());
    }

    zos.closeEntry();
    zos.finish();

  } catch (Exception e) {
    LOGGER.error("エラー発生");
    throw new Exception("エラー発生", e);

  } finally {
    if (zos != null) {
      try {
        zos.close();
      } catch (Exception e) {
        LOGGER.error("エラー発生");
        throw new Exception("エラー発生", e);
      }
    }
  }
}

 * 圧縮処理
 * @param zos 圧縮ストリーム
 * @param filePath 圧縮対象ファイルパス
 * @param fileName 圧縮対象ファイル名
 */
private void archive(ZipOutputStream zos, Path filePath, String fileName) throws Exception {

  // 圧縮対象ファイルの読込ストリームを宣言する。
  BufferedInputStream bis = null;

  try {
    // zipエントリを作成する。
    zos.putNextEntry(new ZipEntry(fileName));

    // ファイルの場合
    if (! Files.isDirectory(filePath)) {
      // 圧縮対象ファイルを読み込む。
      bis = new BufferedInputStream(new FileInputStream(filePath.toString()));

      // 圧縮対象ファイルをzipファイルに出力する。
      int readSize = 0;
      byte[] buffer = new byte[1024];
      while ((readSize = bis.read(buffer, 0, buffer.length)) != -1) {
        zos.write(buffer, 0, readSize);
      }
    }
  } finally {
    if (bis != null) {
      bis.close();
    }
    zos.closeEntry();
  }
}

【Java】画像のマスク処理

画像に対して個人情報等をマスクする際に利用します。

private void imageMaskBlack(String imageFile) thorws Exception {

  // 変数を宣言する。
  BufferedImage bimg = null;
  Graphics graphics = null;
  ImageReader jpgReader = null;
  ImageWriter jpgWriter = null;

  try {
    // イメージファイルを読み込む。(ImageIO.readは利用しない方針)
    jpgReader ImageIO.getImageReadersByFormatName("jpeg").next();
    jpgReader.setInput(ImageIO.createImageInputStream(new File(imageFile)));
    bimg = jpgReader.read(0);

    // Graphicsオブジェクトを取得する。
    graphics = bimg.getGraphics();
    graphics.setColor(Color.BLACK);
    // X座標、Y座標、幅、高さを指定し黒色でマスクする。
    // 画像の左上がX座標:0、Y座標:0
    graphics.fillRect(100, 50, 100, 100);
    graphics.drawImage(bimg, 0, 0, null);

    // 品質と解像度指定
    // イメージを書き込むため、ImageIOからjpg用のImageWriterを取得する。
    jpgWriter = ImageIO.getImageWritersByFormatName("jpeg").next();

    // ImageWriterのパラメータ設定
    // パラメータを取得する。
    JPEGImageWriteParam jpgWriteParam = (JPEGImageWriteParam)jpgWriter.getDefaultWriteParam();
    // パラメータに品質指定を行うモードを指定する。
    jpgWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    // パラメータに品質を指定する。(0.0f~1.0f)
    jpgWriteParam.setCompressionQuality(0.5f);

    // ImageWriterのメタデータの設定
    // メタデータを取得する。
    IIOMetadata imageMeta = jpgWriter.getDefaultImageMetadata(new ImageTypeSpecifier(bimg), jpgWriteParam);
    // メタデータからXMLのツリー構造を取得する。
    Element tree = (Element)imageMeta.getAsTree("javax_imageio_jpeg_image_1.0");
    // ツリー構造から要素「JFIF」を取得する。
    Element jfif = (Element)tree.getElementsByTagName("app0JFIF").item(0);
    //DPI指定を行うモードを指定する。
    jfif.setAttribute("resUnits", "1");
    // X座標は300dpiを指定する。
    jfif.setAttribute("Xdensity", 300);
    // Y座標は300dpiを指定する。
    jfif.setAttribute("Ydensity", 300);
    // メタデータにツリー構造を設定し直す。
    imageMeta.setFromTree("javax_imageio_jpeg_image_1,0", tree);

    // これまでに行った設定を元にイメージファイルを出力する。(ImageIO.writeは利用しない方針)
    jpgWriter.setOutput(ImageIO.createImageOutputStream(new File(imageFile)));
    jpgWriter.write(null, new IIOImage(bimg, null, imageMeta), jpgWriteParam);

  } finally {
    if (grahics != null) {
      graphics.dispose();
      graphics = null;
    }
    if (jpgWriter != null) {
      jpgWriter.reset();
      jpgWriter = null;
    }
    if (jpgReader != null) {
      jpgReader.reset();
      jpgReader = null;
    }
    if (bimg != null) {
      bimg.flush();
      bimg = null;
    }
  }
}

【Java】ビルドを自動で行う

build.xmlをantでビルドする作業を自動で行います。

  • 起動バッチ

    @echo off
    set ANT_HOME=D:/Product/apache-ant-1.10.1
    set BUILD_FILE=build.xml
    set TARGET=destcopy
    set PROJECT_HOME=D:/Product/APP/
    cd %PROJECT_HOME%
    if %ERRORLEVEL%==0 (
      echo ビルドを開始します。
      echo ログは%PROJECT_HOME%/build.logに出力しています。
      call %ANT_HOME%/bin/ant -f %BUILD_FILE% -l build.log %TARGET%
    )
    if %ERRORLEVEL%==0 (
      echo ビルドを終了します。
    ) else (
      echo エラーが発生しています。ログを確認してください。
    )
    pause
    
  • build.xml

    <?xml version"1.0" encoding="UTF-8"?>
    <project name="APP-TEST" basedir="." default="destcopy">
    
      <!-- classpath指定 -->
      <path id="classpath">
        <fileset dir="./lib">
          <include name="*.jar" />
        </fileset>
        <fileset dir="./WebContent/WEB-INF/lib">
          <include name="*.jar" />
        </fileset>
    
      <!-- 初期化 -->
      <target name="clean" description="コンパイル済のclassファイルとjarファイルを削除">
        <delete dir="./dest" />
        <delete dir="./bin" />
      </target>
    
      <!-- プロジェクトコンパイル -->
      <target name="compile" depends="clean" description="コンパイル開始">
        <mkdir dir="./bin" />
        <javac debug="on" fork="true" destdir="./bin" srcdir="./src" classpathref="classpath" deprecation="on" encoding="UTF-8" includeantruntime="flase">
          <compilerarg value="-Xlint:unchecked" />
        </javac>
      </target>
    
      <!-- jarファイル作成 -->
      <target name="destcopy" depends="compile" description="jarファイル作成開始">
        <mkdir dir="./dest" />
        <copy toDir="./dest/standalone/deployments/APP-TEST.war">
          <fileset dir="./WebContent" />
        </copy>
        <copy toDir="./dest/standalone/configuration">
          <fileset dir="./jbossconfiguration" />
        </copy>
        <copy toDir="./dest/standalone/deployments/APP-TEST.war/WEB-INF/classes">
          <fileset dir="./bin/" />
        </copy>
        <touch file="./dest/standalone/deployments/APP-TEST.war.dodeploy" />
      </target>
    </project>
    

【bat】バッチファイルで全てのCSVファイル内の特定文字列を編集する

バッチファイルで同階層にあるCSVファイル内のの特定文字列を編集します。

前提条件
編集前のCSVファイル:TEST_yyyyMMddhhmmss.csv
編集後のCSVファイル:after_TEST_yyyyMMddhhmmss.csv
特定文字列:電話番号と仮定し、3桁、4桁、4桁のハイフン区切りに編集
補足:フォーマットは以下の例の通り
      電話番号は空欄の可能性あり
      電話番号の後続列は空欄の可能性あり
例
編集前
1,山田太郎,202-0003,09011112222,男
2,山田次郎,202-0003,,男
3,山田太郎,202-0003,,

編集後
1,山田太郎,202-0003,090-1111-2222,男
2,山田次郎,202-0003,,男
3,山田太郎,202-0003,,
@echo off
setlocal ENABLEDELAYEDEXPANSION
cd %~dp0
set LOG_FILE=%~n0.log
echo [ INFO ] %date% %time% CSVファイルの編集を開始します。 >>%LOG_FILE%

for %%x in (TEST_*.csv) do (
  echo %%x
  set OUTPUT_FILE=after_%%x
  type nul > !OUTPUT_FILE!

  for /f "tokens=1-5 delims=," %%a in (%%x) do (
    set STRTMP=%%d
    if not "!STRTMP!" == "" (
      if "!STRTMP:~0, 3!" == "090" (
        set STRTMP=!STRTMP:~0, 3!-!STRTMP:~4, 4!-!STRTMP:~7, 4!
        echo %%a,%%b,%%c,!STRTMP!,%%e >>!OUTPUT_FILE!
      ) else (
        echo %%a,%%b,%%c,,%%d >>!OUTPUT_FILE!
      )
    ) else (
      echo %%a,%%b,%%c,%%d,%%e >>!OUTPUT_FILE!
    )
  )
)
echo [ INFO ] %date% %time% CSVファイルの編集を終了します。 >>%LOG_FILE%
endlocal
exit /b
ポイント
・delimsでカンマ区切りを指定する。
・tokensで区切った配列のどのインデックスを利用するか決定する。
  配列は%%a~%%eで参照可能。
・値が空欄の場合、配列が詰められる。(ココがややこしい)
  1列目:配列の長さは5
  2列目:配列の長さは4
  3列目:配列の長さは3
・「type nul > !OUTPUT_FILE!」を行うことで、1度初期化してから実行している。
・繰り返し処理中は「setlocal ENABLEDELAYEDEXPANSION」~「endlocal」内で
  変数は「!XXX!」で参照可能。

【Oracle】バッチファイルからSQLPlusを起動しデータ取得

バッチファイルからSQLPlusを起動しデータ取得を行います。
主にエビデンス取得で利用します。
テスト対象の実施前後で取得し、結果ファイルを比較します。

  • 起動バッチ

    @echo off
    set DATE_TIME=%date:~0, 4%%date:~5, 2%%date:~2, 2%%time:~0, 2%%time:~3, 2%%time:~6, 2%
    
    set IP_ADDRESS=192.168.XX.XX
    set PORT_NO=1521
    set SID=XXX
    set USER_ID=XXX
    set PASSWORD=XXX
    set SQL_FILE=outputCsvAllData.sql
    set SPOOL_FILE=select_AllData.csv
    
    rem -s サイレントモード
    rem    プロンプトやログインメッセージを表示しなくなる。
    rem -L 1度だけログイン
    rem    ログインに失敗すると3回まで再試行するが、1回で終了する。
    sqlplus -s -L %USER_ID%/%PASSWORD%@%IP_ADDRESS%:%PORT_NO%/%SID% @%SQL_FILE%
    
    move %SPOOL_FILE% %DATE_TIME%_%SPOOL_FILE%
    
    echo %DATE_TIME%_%SPOOL_FILE%を出力しました。
    pause
    
  • SQLファイル(outputCsvAllData.sql)

    set echo off
    set linesize 9999
    set pagesize 0
    set trimspool on
    set feedback off
    
    spool select_AllData.csv
    @@select_AllData.sql
    spool off
    
  • 「@@」はパス名を含まないファイル名の場合、「呼び出し元のスクリプトのパス」を補完する。

    例)
    以下のフォルダ構成の場合、
    「@@」をつけると呼び出せるが、「@」の場合、エラーとなる。
    ・C:/起動バッチ
    ・C:/test/outputCsvAllData.sql
    ・C:/test/select_AllData.sql
    
  • SQLファイル(select_AllData.sql)

    prompt テーブル名
    SELECT 項目1 || 't' || 項目2 || 't' || 項目3 FROM テーブル名;
    
  • 文字列結合している理由は「【Oracle】SQLPlusで空白除去」を参照。

【bat】ディレクトリ一覧とファイル一覧を取得する

コマンドプロンプトでディレクトリ一覧とファイル一覧を取得します。
パスとファイル名のみ取得できるようにオプションを追加して取得します。

  • ディレクトリ一覧取得

    /b:不要な情報を非表示(サイズや更新時間)
    /s:ディレクトリ内を再帰表示
    /ad:表示属性はディレクトリのみ(/aが属性、/dがディレクトリ)
    
    dir /b /s /ad
    
  • ファイル一覧取得

    /a-d:表示属性はファイルのみ(-をつけることでディレクトリを除く)
    
    dir /b /s /a-d
    
  • ディレクトリ一覧とファイル一覧取得

    dir /b /s
    

【Oracle】データファイルとコントロールファイルを利用したインポート

データファイルとコントロールファイルを利用して
Oracleにデータをインポートします。

  • バッチファイル
  • @echo off
    REM == バッチファイル起動ディレクトリ取得 ==
    SET BAT_PASS=%~dp0
    
    REM == データファイルパス設定 ==
    SET DATA_PASS=%BAT_PASS%\data\
    
    REM == コントロールファイルパス設定 ==
    SET CTL_PASS=%BAT_PASS%\ctl\
    
    REM == データファイル名 ==
    SET CSVEXT=.csv
    SET CTLEXT=.ctl
    SET LOGEXT=.log
    SET DATA_ANKEN_KANRI=t_anken_kanri
    
    echo ==============================
    echo 案件管理 データファイル取込
    echo ==============================
    sqlldr USERID='TESTUSR/PASSWORD@サービス名' CONTROL='%CTL_PASS%%DATA_ANKEN_KANRI%%CTLEXT%' DATA='%DATA_PASS%%DATA_ANKEN_KANRI%%CSVEXT%' LOG='%DATA_PASS%%DATA_ANKEN_KANRI%%LOGEXT%'
    
    pause
    
  • コントロールファイル
  • OPTIONS (ERRORS=0, ROWS=10000, DIRECT=TRUE)
    LOAD DATA
    INFILE './t_anken_kanri*.csv' "str '\r\n'"
    APPEND
    INTO TABLE "TESTUSR"."T_ANKEN_KANRI"
    FILEDS TERMINATED BY ','
    OPTIONALLY ENCLOSED BY '"' AND '"'
    DATE FORMAT "YYYY/MM/DD hh24:mi:ss"
    TRAILING NULLCOLS
    (
        項目名1
        項目名2
        項目名3
    )
    
  • データファイル
  • "aaaa","bbbb","cccc"
    

【Java】指定日付の月末月初を求める

Java8のLocalDateクラスを利用して指定日付の月末月初を求めます。

// フォーマット
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
// 月初
LocalDate ldFirst = LocalDate.parse("20181015", dtf).withDayOfMonth(1);
// 月末
LocalDate ldLast = LocalDate.parse("20181015", dtf).withDayOfMonth(1).plusMonth(1).minusDays(1);

// 文字列変換
String strFirst = dtf.format(ldFirst);
String strLast = dtf.format(ldLast);

【MySQL】トランザクション管理

MySQLのトランザクションについてのメモ。
後でまとめる。

◆REPEATABLE READ
ファントムリード
スナップショットを保持している場合、発生しない。
スナップショットを保持していない場合、発生する。

ファジーリード
スナップショットを保持している場合、発生しない。
スナップショットを保持していない場合、発生する。

◆READ COMMITTED
ファントムリード
発生する。
ファジーリード
発生する。
スナップショットを保持しない。

MySQLは「REPEATABLE READ」の場合、
「START TRANSACTION;」時点でトランザクションが開始されるわけではなく、
最初のSELECTでトランザクションが開始される。
利点でもあり、欠点でもある。

ファントムリードは追加レコードを参照してしまうこと。
ファジーリードは更新レコードの値を参照してしまうこと。

【Oracle】統計情報に関するSQL

統計情報に関するSQLのメモです。

  • テーブルの統計情報を取得

    EXEC DBMS_STATS.GATHER_TABLE_STATS('スキーマ名', 'テーブル名');
    
  • インデックスの統計情報を取得

    EXEC DBMS_STATS.GATHER_INDEX_STATS('スキーマ名', 'インデックス名');
    
  • テーブルの統計情報の状態確認

    SELECT UT.TABLE_NAME, UT.NUM_ROWS, UT.BLOCKS, UT.AVG_ROW_LEN, UT.SAMPLE_SIZE, UT.LAST_ANALYZED FROM USER_TABLES UT WHERE UT.TABLE_NAME NOT IN ('CHAINED_ROWS', 'CREATE$JAVA$LOB$TABLE', 'EXCEPTIONS', 'PLAN_TABLE', 'PROF$PLAN_TABLE') AND UT.TABLE_NAME NOT LIKE 'BIN$%' ORDER BY UT.TABLE_NAME; 
    
  • インデックスの統計情報の状態確認

    SELECT UI.TABLE_NAME, UI.INDEX_NAME, UI.NUM_ROWS, UI.SAMPLE_SIZE, UI.LAST_ANALYZED, UI.STATUS FROM USER_INDEXES UI WHERE UI.TABLE_NAME NOT IN ('CHAINED_ROWS', 'CREATE$JAVA$LOB$TABLE', 'EXCEPTIONS', 'PLAN_TABLE', 'PROF$PLAN_TABLE') AND UI.TABLE_NAME NOT LIKE 'BIN$%' ORDER BY UI.TABLE_NAME, UI.INDEX_NAME;