質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Spring

Spring Framework は、Javaプラットフォーム向けのオープンソースアプリケーションフレームワークです。 Java Platform上に、 Web ベースのアプリケーションを設計するための拡張機能が数多く用意されています。

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

Q&A

解決済

1回答

2970閲覧

Spring bootのバージョンアップに伴うバグ修正

poyzoy

総合スコア2

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Spring

Spring Framework は、Javaプラットフォーム向けのオープンソースアプリケーションフレームワークです。 Java Platform上に、 Web ベースのアプリケーションを設計するための拡張機能が数多く用意されています。

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

0グッド

0クリップ

投稿2023/11/24 08:35

実現したいこと

CSV出力ボタンを押したらGoogle Chromだと右上にダウンロードされるようにしたいです。

前提

ここに質問の内容を詳しく書いてください。

現在springbootのバージョンアップ(2.2→2.7)をしているのですが、2.2環境で動いていた機能が意図しない動作をするようになってしまいました。
今起きてしまってる現象はCSV出力という画面上のボタンをクリックするとダウンロードされるのではなくページに表示されるようになってしまってます。以前まではクリックするとよくあるブラウザの右上にダウンロードされる状態だったのですが...

依存関係が悪いのかソースが悪いのか、また現在STSをeclipseで動かしてるのですがその設定がダメなのか分からず立ち往生してます。

発生している問題・エラーメッセージ

The HTTP response header [Content-Disposition] with value [attachment; filename="サバイブ.csv"; filename*=UTF-8''%E3%82%B5%E3%83%90%E3%82%A4%E3%83%96.csv] has been removed from the response because it is invalid java.lang.IllegalArgumentException: The Unicode character [サ] at code point [12,469] cannot be encoded as it is outside the permitted range of 0 to 255

該当のソースコード

JAVA

1コントローラー 2@PostMapping(value = "/download/csv, params="Btn") 3 public ResponseEntity<byte[]> download() throws IOException { 4 HttpHeaders headers = new HttpHeaders(); 5 downloadHelper.addContentDisposition(headers, "サバイブ.csv"); 6 return new ResponseEntity<>(getCsvText().getBytes("MS932"), headers, HttpStatus.OK); 7 } 8 9 10DownloadHelper 11 12import java.io.UnsupportedEncodingException; 13import java.nio.charset.StandardCharsets; 14import org.springframework.http.HttpHeaders; 15import org.springframework.stereotype.Component; 16import org.springframework.web.util.UriUtils; 17 18@Component 19public class DownloadHelper { 20 private static final String CONTENT_DISPOSITION_FORMAT 21 = "attachment; filename=\"%s\"; filename*=UTF-8''%s"; 22 public void addContentDisposition(HttpHeaders headers, String fileName) 23 throws UnsupportedEncodingException { 24 String headerValue = String.format(CONTENT_DISPOSITION_FORMAT, 25 fileName, UriUtils.encode(fileName, StandardCharsets.UTF_8.name())); 26 headers.add(HttpHeaders.CONTENT_DISPOSITION, headerValue); 27 } 28 29} 30 31

試したこと

サバイブをtestなどアルファベットにすると以前と同じようにダウンロードが始まります。

補足情報(FW/ツールのバージョンなど)

springframeworkは5.3.30
バージョンアップに伴いspring-webは5.2.2→5.3.30に変更になってました。

springbootは2.2.2→2.7.17です。

何か試してみたら良いことや解決策がある方是非ご教授お願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

要約: ご質問のコードでの、ファイルダウンロードの際のファイル名の処理は誤っているので、コードを修正すべきです。

The HTTP response header [Content-Disposition] with value [attachment; filename="サバイブ.csv"; filename*=UTF-8''%E3%82%B5%E3%83%90%E3%82%A4%E3%83%96.csv] has been removed from the response because it is invalid

java.lang.IllegalArgumentException: The Unicode character [サ] at code point [12,469] cannot be encoded as it is outside the permitted range of 0 to 255

書いてある通り、ヘッダに使われるバイトのコード値として許される範囲 (0-255) を超えているのでエンコードできません。要は、Unicode文字「サバイブ」をそのまま出力しようとしているので「そんなことはできない」と言っています。そして、エンコードできない内容を含んでいるContent-Dispositionヘッダフィールドを出力から削除したと言っています。このため「Content-Disposition: attachment」の指定がされず、受信側ではファイルダウンロードではなくファイル内容をブラウズする動作をしています。

Springの以前のバージョンでは上のような内容のヘッダフィールドも適当に出力されてしまっていたのかもしれませんが、それはウェブアプリケーションとして正しい動作ではありません。ですからSpringは悪くないです。アプリケーションのコードを直しましょう。

少し説明します。ご質問のコード

java

1 private static final String CONTENT_DISPOSITION_FORMAT 2 = "attachment; filename=\"%s\"; filename*=UTF-8''%s"; 3 public void addContentDisposition(HttpHeaders headers, String fileName) 4 throws UnsupportedEncodingException { 5 String headerValue = String.format(CONTENT_DISPOSITION_FORMAT, 6 fileName, UriUtils.encode(fileName, StandardCharsets.UTF_8.name())); 7 headers.add(HttpHeaders.CONTENT_DISPOSITION, headerValue); 8 }

この箇所がファイル名をUnicode文字のまま渡している元凶ですね。次のようなフィールドを作成しようとしています。

Content-Disposition: attachment; filename="サバイブ.csv"; filename*=UTF-8''%E3%82%B5%E3%83%90%E3%82%A4%E3%83%96.csv

元々は、Content-DispositionヘッダフィールドのfilenameパラメータをRFC 6266に準拠するようエンコードする意図があったはずです。ところが2015年当時、RFC 6266に非準拠だったユーザエージェントも救うために、filenameフィールドを重複させてエンコードなしとエンコード済みの両方を含めるというバッドノウハウを提案した人がいたようです (「ねぎぶログ: ダウンロードファイル名、文字化けとの格闘」)。これがその後Qiitaで紹介されてQiitaやteratailで無批判にコピペされることが繰り返され、バッドノウハウであるという情報は抜け落ちたまま広まったと考えられます (コピペ例 1 2 3)。

今では事実上全てのユーザエージェントがRFC 6266準拠の動作をするはずですから、今さら10年近く前に提案されたバッドノウハウに頼る必要はないでしょう。素直にRFC 6266通りのエンコードをすればいいです。

java

1@Component 2public class DownloadHelper { 3 private static final String CONTENT_DISPOSITION_FORMAT 4 = "attachment; filename*=UTF-8''%s"; 5 public void addContentDisposition(HttpHeaders headers, String fileName) 6 throws UnsupportedEncodingException { 7 String headerValue = String.format(CONTENT_DISPOSITION_FORMAT, 8 UriUtils.encode(fileName, StandardCharsets.UTF_8.name())); 9 headers.add(HttpHeaders.CONTENT_DISPOSITION, headerValue); 10 } 11}

次のようなフィールドを作成するようになります。

Content-Disposition: attachment; filename*=UTF-8''%E3%82%B5%E3%83%90%E3%82%A4%E3%83%96.csv

なお、UriUtils.encode()の仕様がよくわからなかったのですが、ファイル名に"が含まれていてもエンコードされなかったりしないでしょうか。もしもそうならReflected file downloadという攻撃への脆弱性につながるため、念のためエンコードした形に置換しています (参考:「Content-Disposition の filename という地雷」(原文))。 考えすぎでした。filename*形式のみ使う場合は問題なさそうなのでこの点は撤回します。ただエンコードが適切かどうかは精査して再度手を加えるかもしれません。

投稿2023/11/25 06:22

編集2023/11/26 00:23
ikedas

総合スコア4443

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

poyzoy

2023/11/26 09:49

誠にありがとうございます! 私のサーチ力ではとても解決できませんでした。 ikedas様の方法で解決できました。 本当に助かりました。 ありがとうございます😭 また、質問見かけましたら是非お力添えお願いします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問