ファイル名のマルチバイト文字対応

2011年1月2日 at 22:09

5.4.1.1がひとまずリリースに至りましたね(まだ問題もあるようですが)。開発陣の皆さんお疲れ様でした。

一段落したところで(早い)、タイトルの件についての提案です。(日本語URLへの対応にも関連して…)
ファイルマネージャに日本語などマルチバイト文字を含むファイルをアップロードすると、現状ではマルチバイト文字が削除されて登録されます。(「写真1.jpg」 → 「1.jpg」 のように)
開発者 or 管理者の立場からいえば、マルチバイト文字を含むファイル名はトラブルのもとというのは当たり前なのですが、それをconcrete5で構築したサイトのユーザーひとりひとりにまで強制するのは困難だと思いますし、やはりマルチバイト文字を使えたほうが便利には違いないでしょう。でもサーバーにはマルチバイト文字を含むファイル名をもつファイルを置きたくない。

というわけで、試しにスクリプトに改造を施してみました。(5.4.1.1用です)
http://hira.hopto.org/software/concrete5/mb_filename.xhtml
(私のWebサイトです[concrete5製じゃないです;;]。こことは別名義でやっています)
PEARのNet_IDNA2ライブラリを用いて、オリジナルのファイル名をPunycodeでエンコードしてサーバーに置く方法を採用しています。Punycodeを使った理由は

1. 既存システムとも互換性がとれる(英数文字のみのファイル名ならエンコードしてもそのまま)
2. ファイル名が長くなりすぎない(URLエンコードでも1.は満たしますが、ファイル名のバイト数が最大3倍になってしまうし、%を含むファイル名だとURLに変換するときにさらにURLエンコード(% → %25)が必要になって面倒)

というものです。既存の枠組みを活かしながらなので実装が少々雑ではありますが、皆さんのご意見をお聞かせいただければ幸いです。
(これってむしろ本家フォーラムに投稿すべきことのような気もしますが)

添付ファイルは上記URLで配布しているZIPファイルです。

添付: mb_filename.zip

Re: ファイル名のマルチバイト文字対応

2011年1月3日 at 1:22
実は5.4.0.5の時にyamanoiさんが実装されましたが、影響範囲などの確認の時間が取れずに保留になっています。

たしかPunycodeとかを使う物では無かった様な気がします。

個人的にはサーバ上にはハッシュ等のユニークな名前で保存し、ダウンロード時に設定された元のファイル名でダウンロードする様にした方がよいかな?と考えています。

実は日本語のURLについてもずいぶん前に「urlencodeではなく、Punycodeではどうか?」という議論がありました。
Punycodeは%が入らなくて短く出来る点は良いのですが、エンコード、デコードにライブラリが必要でPHP標準関数では対応出来ない事と、そのために若干動作が重くなる事からURLencodeにしました。(URLはページを表示する度に何回も処理される部分なので...)

今回の5.4.1.1でも日本語ファイル名の対応は議題にあがりましたが、時間切れで次期バージョンに保留となってしまいました。
なので、もし可能であればconcrete5版のアドオンなりパッチなりを作成して頂き、次期バージョンのリリースまでに検証出来れば嬉しいです。
 

Re: Re: ファイル名のマルチバイト文字対応

2011年1月3日 at 1:40
個人的にはサーバ上にはハッシュ等のユニークな名前で保存し、ダウンロード時に設定された元のファイル名でダウンロードする様にした方がよいかな?と考えています。

元のファイル名でダウンロードさせることについては、現に上で提示したものもそのような仕様にしてあります。
ただ、ハッシュを適用した名前でサーバー上に保存すると、既存システムに適用したときにファイルが呼び出せなくなると思い、現状では「Punycodeのエンコード結果を利用」という形にしています。

実は日本語のURLについてもずいぶん前に「urlencodeではなく、Punycodeではどうか?」という議論がありました。
Punycodeは%が入らなくて短く出来る点は良いのですが、エンコード、デコードにライブラリが必要でPHP標準関数では対応出来ない事と、そのために若干動作が重くなる事からURLencodeにしました。(URLはページを表示する度に何回も処理される部分なので...)

確かに重さの点は重要ですね。現段階での仕様としては、データベースにはマルチバイト入りのファイル名をそのまま格納しており、サーバー上のファイルを呼び出すときにその都度変換を施しています(よってデコード処理を一切使っていません)。データベースの構造を変えなくていいので楽ですが、負荷の面は考える必要がありますね。
ただ、日本語URLと違い、ファイル名の場合はurlencodeだとファイルシステムの限界(255バイトなど)を簡単に超えそうで怖いのです(日本語1文字に対して、UTF-8では典型的には3バイトで、1バイトが %81 のように変換されるので、エンコード結果は都合9バイトになりますね。30文字弱で限界を超えます)。もちろんサーバー上のファイル名としてハッシュ関数の結果を使うならこの限りではありませんが。

今回の5.4.1.1でも日本語ファイル名の対応は議題にあがりましたが、時間切れで次期バージョンに保留となってしまいました。
なので、もし可能であればconcrete5版のアドオンなりパッチなりを作成して頂き、次期バージョンのリリースまでに検証出来れば嬉しいです。

そうですね、エンコード方法の選択やその長短も含めて議論の余地があるでしょうから、次期バージョンのリリースまでにゆっくりと、という感じになるでしょうか。
 

Re: Re: ファイル名のマルチバイト文字対応

2011年2月22日 at 18:24
山野井です。

以前のバージョンでハックしたものです。
最後にあるdownload()変更し、直接読むように変更しました。
http://www.yamanoi.org/concrete5/hack/download/
 

Re: ファイル名のマルチバイト文字対応

2011年1月3日 at 1:50
お。

実は、私たちも、2バイトファイル名を、そのままサーバー上に保存することを考えましたが、サーバーによってはファイル名がえらいことになって見栄えが悪いので、保留にしていました。

今、本家にプッシュしたいことは、マルチバイトURLを、本家のコアーに取り入れてもらうことです。そうすることによって、日本語版リリースのタイムラグが短くなりますし、我々のメンテの量が少なくなりますから。

日本語チームだけが独自にメンテする機能を増やしまくると、またリリースのタイムラグが出てくるので、私個人的では日本語URLを、まず、プッシュして、その結果を観たいと思います。



ここで言っているのは、機能とか技術の問題ではなく、コアーに取り入れられるだけのアピールをすることが出来るかの「営業」です。



ファイルの日本語表記も同じで、本家で、もっと日本のユーザーの要望を取り入れてもらうようなアピールをしていかなくてはいけません。

基本的に、私が、ここのフォーラムで発言していることは、私の許可なしに、「Katz が日本語フォーラムでこう言っていたよ〜」と勝手に通訳して発言していただいてもかまいませんので、本家へのアピールの協力をよろしくお願いします。

既にパッチを作成していただいたので、問題がなければ、採用したい方向にしたいです。
が、問題は、今後の将来です。

本家が「イヤ」と言った時や、他の方法をとったとき、アップグレードスクリプトを書かなくてはいけません。

そこまで責任をもってやれるかです。



ちなみに、開発では、日本語のSVN(というかGITに移行しようかと考え中)のアカウントを差し上げます。んで、コミット権限を持っているユーザー同士で、Skype会議室を持っていますんで、そこでコミットする旨などの連絡をとりあって進めています。

てな感じです。
 

Re: Re: ファイル名のマルチバイト文字対応

2011年1月3日 at 2:05
私も、日本語URLの動向を見てからでも遅くないと思います。本家との乖離が進み過ぎると危ないですしね。

「営業」というのは要するに、Katzさんが別スレッドでおっしゃっていた、本家にマルチバイト対応の重要性をわからせる、ということですね。
さしあたり、私も本家フォーラムに参戦し、その「別スレッド」の「ユーザー属性の表示をハンドル名から名前に戻せ」という件について、(私が実際に困ったということもありますので)投稿してみました。(あんな感じでいいんでしょうかね?) レイアウトの件については、自分が使ってないのでノーコメントという感じですが…。

とにかく、ひとたび参戦したので、後は本家フォーラムで色々言うのもそれほど苦にはならないかも…と思っています(英語の問題はさておき)。可能な範囲で協力させていただこうと思いますので、よろしくお願いいたします。
 

Re: Re: ファイル名のマルチバイト文字対応

2011年2月18日 at 17:44
ファイルマネージャで、チェックを付けて上部のコンボボックス「ダウンロード」をクリックするとZIPファイルとしてダウンロードできるようですが、PHP Zip (ZipArchiveクラス)が使えるときは使うように変更してみました。WindowsのXAMPPでも動くようになりました。
http://hira.hopto.org/software/concrete5/mb_filename.xhtml

そのときに気づいたのですが、現状ではzipコマンドで圧縮するとき、格納するファイル名がエンコードされた名前になってしまっています。
とりあえずPHP Zipを使うときは日本語名対応を済ませましたが、PHP Zipが使えなくてzipコマンドで圧縮するときは、どうしたものかと思案しております。いちいちファイルをコピーするのも考えものですし…。
 

Re: Re: Re: ファイル名のマルチバイト文字対応

2011年2月18日 at 18:17
これ、MacやUnix使いの人たちは、Shift JISエンコードで圧縮されちゃうと、私たちが文字化けになっちゃう可能性ありですか?

ますます、ファイルマネージャー日本語対応を公式で行うことが怪しくなってきたという印象です。

お・・・そうだ。 site.php で、日本語エンコードの種類を define 設定できるようにするのもいいかもしれませんね。

そうだそうだ。それで解決するわ。
 

Re: Re: Re: Re: ファイル名のマルチバイト文字対応

2011年2月19日 at 0:54
これ、MacやUnix使いの人たちは、Shift JISエンコードで圧縮されちゃうと、私たちが文字化けになっちゃう可能性ありですか?

ますます、ファイルマネージャー日本語対応を公式で行うことが怪しくなってきたという印象です。


正直言って、ZIP圧縮ダウンロードなどという機能があることに今日気づきました。自分でやっときながら、まさかこうも面倒なことになるとは、という感じです。

PHP Zipを使う場合はいいですけど、zipコマンドを使う場合は格納名がサーバーの文字エンコーディングに依存したりするんですかね?(よくわかりません)
心配事はクライアントOSばかりではないのかもしれません。だんだん訳が分からなくなってきました…。

お・・・そうだ。 site.php で、日本語エンコードの種類を define 設定できるようにするのもいいかもしれませんね。

そうだそうだ。それで解決するわ。


クライアントのOS判定などをしてみてもいいかもしれませんね。Windowsでアクセスした人にはShift-JISエンコーディングのZIPを返して、LinuxだとUTF-8にするとか(LinuxだからUTF-8という決め打ちもどうかと思いますが、例えばの話)。
その辺は後からまた練りましょうsmiling

他の方も、いい案があれば教えてください。ちょっと行き詰まってるところですんで;;
 

Re: ファイル名のマルチバイト文字対応

2011年2月4日 at 15:18
日本語ファイル名の件とふと思い出したので、上げておきます。

・本家にもっと働きかける
・日本語版に取り入れる
・パッケージ化(今のところ仕様で無理だけど)
・ハックとして紹介する

の道が考えられます。

本当は、やりたいんだけれどね〜。
 

Re: ファイル名のマルチバイト文字対応

2011年5月1日 at 11:43
マルチバイト化でちょっと違ったアプローチをしたとこと、簡単にファイル名のマルチバイト化ができましたので、参考までお知らせします。
具体的には、/concrete/helpers/file.php を /helpers/file.php
にコピーし、クラス名をSiteFileHelperに変えます。
変更するファンクションは
public function forceDownload($file)
public function sanitize($file)
の2つだけです。追加するコードも合わせて20行ほどで済みました。

public function forceDownload($file)
では
$filename = basename($file);
の行の後に、
$useragent = $_SERVER['HTTP_USER_AGENT'];
if (strstr($useragent, 'Windows') !== false ||
strstr($useragent, 'Mac_') !== false) {
$filename = mb_convert_encoding($filename, 'SJIS', APP_CHARSET);
}
の5行を加えます。

public function sanitize($file)
では
$file = preg_replace(array("/[\s]/","/[^0-9A-Z_a-z-.]/"),array("_",""), $file);
の行をコメントアウトして以下の15行を加えます。
$file = preg_replace("/[\s]/","_", $file);
if(strpos($_SERVER['HTTP_USER_AGENT'], 'Windows') == false)
$separater = '/';
else
$separater = '\\';

$len = strlen($file);
$i = $len-1;
while ($i > 0) {
if (substr($file, $i, 1) == $sepalater) break;
$i--;
}
if ($i != 0) $i++;
$newlen = $len - $i;
$file = substr($file, $i, $newlen);

Windowsのサーバ環境では試していませんが、Linuxのサーバ環境では
これでOKでした。
 

Re: Re: ファイル名のマルチバイト文字対応

2011年5月5日 at 14:15
yamanoiです。

safariでもOKでしたか。
 

Re: Re: Re: ファイル名のマルチバイト文字対応

2011年5月5日 at 20:22
yamanoiさんご指摘ありがとうございます。

試してみたら、Safariでのダウンロードでファイル名が文字化けしました。
いろいろ試してみたら、SafariはUTF-8でファイル名を受け取るのですね、
ということで、やっつけ

forceDownload($file)の
if (strstr($useragent, 'Windows') !== false ||
strstr($useragent, 'Mac_') !== false) {
$filename = mb_convert_encoding($filename, 'SJIS', APP_CHARSET);
}の後に
if(strstr($useragent, 'Safari')) $filename = basename($file);
の1行を加えてうまくいきました。