【Java】PDFからJPG変換

PDFファイルに利用されている画像をJPG変換します。
以下のライブラリが必要です。
pdfbox-2.0.7.jar
commons-logging-1.2.jar

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;

import javax.imageio.ImageIO;

import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageTree;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.graphics.PDXObject;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;

public class TestPDFBoxPdfToJpg {

    public static void main(String[] args) {
        //PDFドキュメントをロード
        try (PDDocument document = PDDocument.load(new File("D:\\test\\pdf\\e-ticket.pdf"));) {

            //ページのリストから最初の1ページを取得する
            PDPageTree pageTree = document.getDocumentCatalog().getPages();
            PDPage page = pageTree.get(0);

            //ページからリソースを取得し、最初のイメージを取得する。
            PDResources resources = page.getResources();
            Iterator<COSName> ite = resources.getXObjectNames().iterator();

            int i = 0;

            while (ite.hasNext()) {

                COSName name = ite.next();

                //取得したイメージをファイルに出力
                PDXObject xobject = resources.getXObject(name);

                if (xobject instanceof PDImageXObject) {
                    PDImageXObject image2 = (PDImageXObject) resources.getXObject(name);
                    ImageIO.write(image2.getImage(), "jpg", new FileOutputStream("D:\\test\\pdf\\e-ticket" + i + ".jpg"));
                }

                i++;
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【Java】例外内容を文字列で取得する

例外内容を文字列で取得します。
例外内容をメール本文等に出力する際に利用します。

/** 
 * 例外内容取得処理
 * @param 例外オブジェクト
 * @return 例外内容文字列
 */
public static String getTrace(Throwable th) {

    StringBuilder sb = new StringBuilder();
    String lineSeparator = System.getProperty("line.separator");

    if (th != null) {
        sb.append(th.getClass().getName() + ":" + th.getMessage());
        StackTraceElement[] stack = th.getStackTrace();
        if (stack != null) {
            for (int i = 0; i < stack.length; i++) {
                sb.append(lineSeparator);
                sb.append("\tat " + stack[i].toString());
            }
        }
        sb.append(lineSeparator);

        Throwable causeTh = th.getCause();
        String caused = getCaused(causeTh);
        sb.append(caused);
    }

    return sb.toString();
}

/** 
 * 例外内容取得処理(原因)
 * @param 例外オブジェクト
 * @return 例外内容文字列
 */
public static String getCaused(Throwable th) {

    StringBuilder sb = new StringBuilder();
    String lineSeparator = System.getProperty("line.separator");
    Throwable chainTh = null;

    if (th != null) {
        sb.append("Caused by: " + th.getClass().getName() + ":" + th.getMessage());
        StackTraceElement[] stack = th.getStackTrace();
        if (stack != null) {
            for (int i = 0; i < stack.length; i++) {
                sb.append(lineSeparator);
                sb.append("\tat " + stack[i].toString());
            }
        }
        sb.append(lineSeparator);

        chainTh = th.getCause();

    } else {
        return sb.toString();
    }

    return sb.append(getCaused(chainTh)).toString();
}

/**
 * テストドライバ
 * @param args
 */
public static void main(String[] args) {

    try {
        test();
    } catch (Throwable th) {
        System.out.println("JavaAPIで例外出力");
        th.printStackTrace();

        System.out.println();
        System.out.println("独自APIで例外出力");
        System.out.println(getTrace(th));
    }
}
public static void test() throws Exception {
    try {
        try {
            int i = 0;
            int j = 100;
            int k = j/i; // ここでエラー発生
        } catch (Exception e) {
            throw new Exception("エラー", e);
        }
    } catch (Exception e) {
        //throw e;
        throw new Exception("エラー2", e);
    }
}

【Java】BATからJava呼び出し

BATファイルからJava処理を呼び出します。

@echo off
REM 空欄を指定した場合、後続処理でバッチ実行ディレクトリになる
set USR_EXEDIR=C:\work\bin

echo START Java呼び出し

if "%USR_EXEDIR%" EQU "" (set USR_EXEDIR=%~dp0)
cd /d %USR_EXEDIR%
if %errorlevel% == 1 goto label_cderror

set LIB_DIR=C:\work\lib
set EXE_PATH=.\sub.jar;..\db\ojdbc7.jar;%LIB_DIR%test.jar
set CONF_DIR=..\conf
set CLASS_PATH=.

set JAVA_OPTION=-Xms128M -Xmx128M -XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=64M
java %JAVA_OPTION% -classpath %EXE_PATH%;%CONF_DIR%;%CLASS_PATH%;jp.co.test.TestMain > nul

echo END Java呼び出し(0)
exit %errorlevel%

:label_cderror
echo END Java呼び出し(9)
exit(9)

【Java】フォルダ解凍処理

フォルダ内のファイルを解凍します。

/** 
 * ファイル解凍処理
 */
private void deCompressFile() throws Exception {

  // 圧縮ストリームを宣言する。
  ZipInputStream zis = null;

  try {
    // 作業フォルダのファイルを解凍する。
    zis = ZipInputStream(
      new BufferedInputStream(
        new FileInputStream(this.currentZipFileName)));

    // ファイルのエントリを宣言する。
    ZipEntry entry;
    // ファイルに含まれるエントリ分繰り返す。
    while ((entry = zis.getNextEntry()) != null) {
        // 解凍対象ファイル名を取得する。
        String fileName = entry.getName();
        // ディレクトリの場合
        if (entry.isDirectory()) {
            // ディレクトリを作成する。
            Path filePath = Paths.get(this.currentTmpDir + Const.FILE_PATH_SEPARATOR + fileName);
            Files.createDirectories(filePath);
        // ファイルの場合
        } else {
            // 解凍処理を行う。
            extract(zis, fileName);
        }
    }
  } catch (Exception e) {
    LOGGER.error("エラー発生");
    throw new Exception("エラー発生", e);

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

 * 解凍処理
 * @param zis 圧縮ストリーム
 * @param fileName 解凍対象ファイル名
 */
private void extract(ZipInputStream zis, String fileName) throws Exception {

  // 出力用のストリームを宣言する。
  BufferedOutputStream bos = null;

  try {
    // 解凍対象ファイルのフルパスを取得する。
    Path filePath = Paths.get(this.currentTmpDir + Const.FILE_PATH_SEPARATOR + fileName);

    // 解凍対象ファイルを読み込む。
    int readSize = 0;
    byte[] buffer = new byte[1024];
    bos = new BufferedOutputStream(
        new FileOutputStream(filePath.toString()), 1024);

    // 解凍対象ファイルをファイルとして出力する。
    while ((readSize = zis.read(buffer, 0, buffer.length)) != -1) {
        bos.write(buffer, 0, readSize);
    }
    bos.flush();

  } finally {
    if (bos != null) {
      bos.close();
    }
    zis.closeEntry();
  }
}

【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>
    

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

【Java】マルチスレッド処理

Javaのスレッド処理に関するメモ。
呼び出す側だけを変更すれば対応可能です。
今回の例では1ファイルをタスクに渡しているので、
大量ファイルで1ファイル内のレコード量が少ない時に効果が出ます。
1ファイルで1ファイル内のレコード量が多い場合、タスクに渡す単位を考えた方が良いです。

  • 呼び出す側
    public static void main(String[] args) {
        Z_Thread main = new Z_Thread();
        // スレッド管理サービスを生成する。
        ExecutorService multiExecutor = Executors.newFixedThreadPool(2);
        main.executeMultiTask(multiExecutor);
        // スレッドを終了する。
        multiExecutor.shutdown();
    }
    
    private void executeMultiTask(ExecutorService serviceExecutor) {
        // 戻り値を定義する。
        List<Future<Long>> futures = new ArrayList<Future<Long>>();
        Future<Long> future = null;
        try {
            String fileDir = "D:\\";
            String fileName = "";
            // 指定ディレクトリの「*.txt」の一覧を取得する。
            Path path = Paths.get(fileDir);
            try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path, "*.txt")) {
                // 指定ディレクトリの「*.txt」数分繰り返す。
                for (Path p : directoryStream) {
                    // ファイル名を取得する。
                    fileName = p.getFileName().toString();
                    // 取得したファイル名をタスクに登録する。
                    // タスクはCallableを利用することで戻り値と例外を受け取れるようにする。
                    // タスクはRunnableを利用すると戻り値と例外は受け取れない。
                    Future<Long> future = serviceExecutor.submit(new CallableTest(fileDir, fileName));
                    futures.add(future);
                }
            } catch (IOException e) {
                System.out.println("IO例外");
            }
            // 結果を順次取得する。
            for (Future<Long> futureResult : futures) {
                Long result = futureResult.get(20000, TimeUnit.MILLISECONDS);
                System.out.println("結果:" + result + ":スレッドの結果を受け取りました。");
            }
        } catch (InterruptedException e){
            System.out.println("割り込み例外");
        } catch (ExecutionException e) {
            System.out.println("タスクの中で例外発生");
        } catch (TimeoutException e) {
            System.out.println("タイムアウト例外発生");
        } catch (Exception e) {
            System.out.println("何かの例外発生")
        } finally {
            future.cancel(true);
        }
    }
    

【Java】シングルスレッド処理

Javaのスレッド処理に関するメモ。
JDK1.4ではThreadを利用していたが、JDK1.5以降はExecutorServiceを利用する。

  • 呼び出す側
    public static void main(String[] args) {
        Z_Thread main = new Z_Thread();
        // スレッド管理サービスを生成する。
        ExecutorService singleExecutor = Executors.newSingleThreadExecutor();
        main.executeSingleTask(singleExecutor);
        // スレッドを終了する。
        singleExecutor.shutdown();
    }
    
    private void executeSingleTask(ExecutorService serviceExecutor) {
        // 戻り値を定義する。
        Future<Long> future = null;
        try {
            String fileDir = "D:\\";
            String fileName = "";
            // 指定ディレクトリの「*.txt」の一覧を取得する。
            Path path = Paths.get(fileDir);
            try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path, "*.txt")) {
                // 指定ディレクトリの「*.txt」数分繰り返す。
                for (Path p : directoryStream) {
                    // ファイル名を取得する。
                    fileName = p.getFileName().toString();
                    // 取得したファイル名をタスクに登録する。
                    // タスクはCallableを利用することで戻り値と例外を受け取れるようにする。
                    // タスクはRunnableを利用すると戻り値と例外は受け取れない。
                    future = serviceExecutor.submit(new CallableTest(fileDir, fileName));
                    // Futureクラスのgetメソッドを呼び出すと呼び出し側は結果が返ってくるまで処理を止める。
                    // 20秒を過ぎるとタイム例外が返ってくる。
                    Long result = future.get(20000, TimeUnit.MILLISECONDS);
                    System.out.println("結果:" + result + ":スレッドの結果を受け取りました。");
                }
            }
        } catch (InterruptedException e){
            System.out.println("割り込み例外");
        } catch (ExecutionException e) {
            System.out.println("タスクの中で例外発生");
        } catch (TimeoutException e) {
            System.out.println("タイムアウト例外発生");
        } catch (Exception e) {
            System.out.println("何かの例外発生");
        } finally {
            future.cancel(true);
        }
    }
    
  • 呼び出される側(タスク)(Callableを利用する)
    private String fileDir = "";
    private String fileName = "";
    
    public CallableTest(String fileDir, String fileName) {
        this.fileDir = fileDir;
        this.fileName = fileName;
    }
    
    public Long call() throws Exception {
        try {
            System.out.println(Thread.currentThread().getId() + ":スレッド処理開始");
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("スレッドに割り込みありました。");
                throw new InterruptedException("割り込み例外");
            }
    
            // 何かの処理を想定
            Thread.sleep(10000);
            // コンストラクタで指定したファイルの内容を読み取り表示する。
            List<String> line = Files.readAllLines(Paths.get(fileDir, fileName), Charset.forName("UTF-8"));
            System.out.println(Thread.currentThread().getId() + ":" + fileName + "の内容を表示");
            for (String str : line) {
                System.out.println(str);
            }
        } catch(Exception e){
            System.out.println(Thread.currentThread().getId() + "エラー検知");
            throw e;
        } finally {
            System.out.println(Thread.currentThread().getId() + ":スレッド処理終了");
        }
        return Thread.currentThread().getId();
    }