Abstract#
画像ファイルをアップロードするときにファイルサイズを小さくしたい。Topics#
やりたいこと#
- GeoPicPluginでファイルサイズを抑えた写真ファイルのアップロードをしたい。ただし、本当に制限したいファイルサイズも指定したい。例えば、50Mのファイルはダメとか。
記録#
- まずファイルの扱いをフローにしたい。
アップロードボタンが押されてから、まず、サーバにファイルがアップロードされて、そのあとEXIFを読み取るのが今回のプログラムかな。保存のタイミングはどこか。 |
GeoPicServlet.upload()の175ライン当たりにparseRequestというのがあり、ここで”本当に制限したいファイルサイズ”のチェックがされる。 |
GeoPicServlet.upload()の250ラインあたりに(FileItem)actualFileオブジェクトができている。そこから情報を吸い出しているようだ。InputStreamもそこからいつでも得られる。このInputStreamからEXIFなどを吸い出して、Fileを更新して一次フォルダに保管しておいて、ファイルアップロードを実行するときには一次保管したファイルのInputStreamを渡す。 |
315行あたりにthis.executeUpload(,InputStream in,)とある。 |
467行あたりにm_engine.getAttachmentManager().storeAttachment(att, data);とある。 |
AttachmentManagerの574行目にm_provider.putAttachmentData( att, in )がある。これは、WikiAttachmentProviderというInterface Classに定義されているメソッド。実態はどこだ?org.apache.wiki.providersパッケージの中にいくつかある。 |
この(InputStream)dataを圧縮後のファイルに置き換えるということか。 |
- 一時的にファイルを保存したい。
TOMCAT_HOMEの下にtempフォルダがあって、そこにアップロードするファイルが一時的に保存されている。プロパティのworkDirを使っているところを探そう。m_engine.getWorkDir()で取れるっぽい。そこに淡々と保存するか。で、そのファイルを開いて、InputStreamをつくるか。 |
- 一時ファイルの削除
もし一時ファイルに保存したとしたら、それを消すのはいつ?tempFile.deleteOnExit() |
- 画像ファイルからEXIF情報を削除するのはApache Commons Imaging[1]かな。
in は InputStream、Outは、InputStreamってできる?一度Tempファイルで保存か。サンプルを見よう[4]. |
- 画像のファイルサイズの圧縮ができるAPIはあるか?
ImageIOってなんだろう[2]。いくつかサンプルが[3]。BufferredImageが便利。しかしImageオブジェクトをBufferredImageにキャストしようとしたらエラーがでた。同じ問題に当たった人も[5] |
- なんとPNGってEXIF情報保存できないんだ。。。tiffにするか。いやApache Commons Imagingでは、TIFFはEXIFに対応していないように見える[6]。そうするとPNGにして、メタデータは、書誌情報として保存か。本当にそれでいい?ImageIOを使った方法[8]をちょっと調べたうえでやろう。JPEGに越したことはない。
- ファイル形式結論はJPEGで。
Imaging.writeImage(buffered, tempImageFile, ImageFormats.JPEG, null);ImageIOでやるとこうなる。画質が指定できないのが玉に傷だが、シンプルだ。
ImageIO.write(buffered, "jpeg", tempImageFile);
- なんか写真が180度回転して表示される。どうしたものか。EXIFにあるらしい、Orientationの情報をもとに回転させたほうがいいのかな。同じ問題に当たった人も[7]。EXIFのOrientationの値をゲットすると、3とかがでてきた。これは180度回転しているものらしい[9]。graphics2Dのrotateメソッドを使おう。
- ファイルサイズを選択するリストを最初Disableにしないと。(結局選択させないことに)
- Reziseをチェックしないのに動いちゃった。それはまずい。(結局全部リサイズすることに)
- ResizeをするとEXIFが消えるような気がする。当たり前なのだが。EXIF情報をコピーするとかできるのか?(結局全部消すことに)
- ファイルをいじるところをまとめたほうがいいと思う。(一回で済ませた)
- 回転する(EXIF次第)
- Resizeする(必ずやる)
- EXIFを取得する(必ずやる)
- EXIF情報を付加する(やらないことにした)
- EXIF情報を削除する(回転などをやるとあえてコピーしないと消える。コピーの処理はやらないことにした)
- JPEG以外でアップロードされた場合どうするか。いまは、JPEGがPNGで保存。JPEGの時だけ動くようにしたい。(OK)
- JPEGでもアップロードできないファイルがある。Invalid marker found in entropy dataという.ImageReadExceptionがでる。まだこれで解決するかわからないけど、Apacheでも議論があって1.0-alpha2で解決されているという[10]。2020年5月7日時点では、Mavenレポジトリから呼べないのだけど。Githubのレポジトリから最新版をクローンして、コンパイルして1.0-alpha2のJARをゲットして、JSPWikiのWEB-INFのlibフォルダに入れて解決。
- ResizeしないとRotateされない。→もう面倒だから全部Resizeだ。
- ん?JPEGのファイルで、全体に赤とか、黄色とかがかかる現象が。試しにPNG形式で保存しようとしても、同じ結果だ。つまり、ImageIO.writeは悪くない。getScaledInstanceを飛ばしてみても結果は同じ。ここもあまり悪くなさそう。orientationValueが3か6以外の画像も同じ現象が起きたから、graphics2D.rotateも関係なさそうだ。graphics2D.drawImage()を飛ばしても結果は変わらない。Imaging.getBufferedImage(in)が怪しい。ここで得られたBufferedImageをそのままImageIO.writeしても結果は変わらない。ここは、Imaging.getBufferedImage(in)をImageIO.read(in)に代替して、解決
- iphoneだと90度回転がありうる。Orientation Valueとしては、6。この時、widthとheightが入れ替わることおよびその回転の中心座標に注意。
orientation value | 回転角度(°) | 幅 | 高さ | 回転の中心座標 | |
1 | 0 | width | height | 回転しない | |
3 | 180 deg | width | height | (width/2, height/2) | |
6 | 90 deg | height | width | (height/2, height/2) |
Referece#
- [#1]http://commons.apache.org/proper/commons-imaging/
- [#2]https://qiita.com/oohira/items/be99b3c8f025d04a5541
- [#3]エンジニアの入り口, 2018.04.13,【入門者向け】Javaにおける画像の扱い方を分かり易く解説, https://eng-entrance.com/java-image
- [#4]http://commons.apache.org/proper/commons-imaging/sampleusage.html
- [#5]https://stackoverflow.com/questions/13605248/java-converting-image-to-bufferedimage
- [#6]http://commons.apache.org/proper/commons-imaging/formatsupport.html
- [#7]http://mumumu.github.io/blog/2013/05/15/rotate-image-depending-on-exif-orientation-value/
- [#8]@oohira, 2017年4月16日, JavaでJPEG画像の保存品質を変更する, https://qiita.com/oohira/items/be99b3c8f025d04a5541
- [#9]EXIF Tags, https://exiftool.org/TagNames/EXIF.html
- [#10]2019年6月9日, Invalid marker found in entropy data, https://issues.apache.org/jira/browse/IMAGING-134