2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

フリューAdvent Calendar 2023

Day 10

Spring Bootでエクセルファイルをアップロードして読み込む

Last updated at Posted at 2023-12-09

この記事はフリュー Advent Calendar 2023の10日目の記事となります。

はじめに

社内で利用するウェブアプリの機能追加として、エクセルファイルを読み込ませて一括処理を行いたいという要望があったので、実装しました。
オンメモリからエクセルファイルを読み込む方法がレアケースなのか、意外にも他に記事として見かけませんでしたので、私が得た情報をアップロードからの流れに沿ってまとめておこうと思います。

ファイルをアップロードする

フロントエンド、バックエンドそれぞれ説明します。
記載しているソースコードに最低限としており、エラー処理などは入っていませんので実際に利用する際には必要に応じてチェックなど行ってください。

環境

以下の環境で動作を確認しています。

ライブラリ バージョン
Spring Boot 3.2.0
Open JDK 21
JQuery 3.7.1
Apache POI 5.2.3

その他はSpring Bootプロジェクトデフォルトでの環境となっています。

なお、ブラウザはGoogle Chromeのバージョン120で確認しています。

フロントエンド

今回はhtmlとJavaScriptを利用してアップロードを実現していきます。
JavaScriptを利用せずSpringの機能を使いアップロードを実現する方法もありますが、こちらはまた別途紹介したいと思います。

以下はファイル選択および、アップロードボタンを配置してアップロード処理をJavaScriptで記載したhtmlファイルになります。
JavaScriptはMultipartFormデータとしてファイルのアップロードを行います。
Multipartのキー名をバックエンドと合わせる必要があり、ここではupload_fileという文字列を使用しています。

upload.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <script type="text/javascript">
        $(function() {
            $('#uploadButton').on('click', function(event) {
				
                var $file = $('#uploadSelectFileBox');

                // FormData オブジェクトを作成
                var formData = new FormData();
                formData.append('upload_file', $file.prop("files")[0]);

                // Ajaxで送信
                $.ajax({
                    // アップロードを処理するAPIへのURL
                    url: 'file/upload',
                    // HTTPメソッド
                    method: 'post',
                    // レスポンスの形式
                    dataType: 'json',
                    // dataに FormDataを指定
                    data: formData,
                    // dataの変換をさせない
                    processData: false,
                    // contentTypeをfalseにすることで適切なContentTypeを指定してくれる
                    contentType: false
                }).done(function(data, textStatus) {
                    // 送信成功

                    // Jsonが返ってきたら以下のようにパースできる
                    var data_stringify = JSON.stringify(data);
                    var data_json = JSON.parse(data_stringify);

                    // Jsonへのアクセスも簡単にできます
                    console.log(data_json.message);

                }).fail(function(XMLHttpRequest, textStatus, errorThrown) {
                    // 失敗時の処理(エラーメッセージなど)
                    console.log(textStatus);
                });

                return false;        
            });
        });
    </script>
</head>
<body>
    <input type="file" name="file" id="uploadSelectFileBox" accept=".xlsx"/>
    <input type="button" id="uploadButton" value="アップロード" />
</body>
</html>

こんな感じに表示されます。
image.png
これでアップロードボタンを押すと、ファイルがサーバーへ送信されます。
フロントエンドではバックエンドのfile/uploadを呼び出していますので、バックエンド側でこのAPIを用意する必要があります。

バックエンド

バックエンドのControllerを作ります。
エクセルファイルを読み込むにはApache POIライブラリを利用します。
GradleもしくはMavenの設定ファイルに以下の2つのライブラリを追加します。
※バージョン5.2.3での動作確認を行っています

ここで注意したいのはこれらのライブラリは他のライブラリへの依存関係もありますので、ご自身の環境から必要に応じて上記ページのCompile Dependenciesからバージョンの更新や足りないもの確認してください。

コントローラーの処理は以下になります。
こちらも例によってエラー処理などは端折っていますし、DBへの更新処理を行う場合は安全のために入力値の正当性やトランザクション処理を実装してください。
エクセルのセルへのアクセスもサンプルとして記述しています。

FileUploadController.java
package com.upload;

import java.io.ByteArrayInputStream;
import java.util.Date;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;


@Controller
@RequestMapping("/file")
public class FileUploadController {


	private class UploadResponse
	{
		public int status = 1;
		public String message = "success";
	}
	
	@PostMapping(value = "/upload")
	@ResponseBody
    public UploadResponse uploadFile( @RequestParam("upload_file") MultipartFile multipartFile ) throws Exception {

		UploadResponse response = new UploadResponse();

		// ファイルが空の場合は異常終了
		if(multipartFile.isEmpty()){
			// 異常終了時の処理
            response.status = 0;
			response.message = "アップロードされたエクセルファイルが存在しなかった";
			return response;
		}

		//バイナリを取得する
		byte[] bytes = multipartFile.getBytes();
		//バイナリからストリームへ変換
		ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);

		//エクセルをワークブックインスタンスへ
		Workbook wb = WorkbookFactory.create(inputStream);

		// ほげ1という名前のシートを取得
		Sheet sheet = wb.getSheet("ほげ1");

		// 1行目取得
		Row row = sheet.getRow(0);

		// データがある最後のセル番号取得
		final int cellNum = row.getLastCellNum();
		// セル分ループ
		for (int cellIndex = 0; cellIndex < cellNum; ++cellIndex) {
		    //セル取得
		    Cell cell = row.getCell(cellIndex);

		    //セルの値取得
		    switch (cell.getCellType()) {
		        case STRING:
		            String stringVal = cell.getStringCellValue();
		            // 文字列データの処理
		            break;
		        case NUMERIC:
		            if (DateUtil.isCellDateFormatted(cell)) {
		                Date dateVal = cell.getDateCellValue();
		                // 日付データの処理
		            } else {
		                double numVal = cell.getNumericCellValue();
		                // 数値データの処理
		            }
		            break;
		        default:
		            // その他のデータタイプに対する処理
		        	break;
		    }

		    //必要に応じた処理
		}

		response.message = "正常に完了しました";
		return response;
	}
}

実装は以上となります。
ポイントとしては、JavaScript側で指定したupload_fileを指定してアップロードされたファイルを取得している点と、WorkbookFactory.createへはbyte[]では引数に渡せなかったのでストリームとしてByteArrayInputStreamを使ってエクセルを開いています。
セルにアクセスするにはセルに設定されている値の種類を取得し、種類によって処理を振り分ける必要があります。

エクセルの操作内容については以下のように他に詳しい記事がたくさんありますので、そちら参考にしていただくのが良いかと思います。

まとめ

・シンプルなアップロード処理は簡単に実現できます
ByteArrayInputStreamを使えばオンメモリでエクセルファイルを開くことができる
Apache POIを使えば簡単にエクセル操作が可能になります

ライブラリがあるおかげで実装量も少なくアップロードからエクセル操作まで実装できました。
次回、Spring BootとThymeleafを利用したアップロード方法も紹介したいと思います。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?