2018/12/08(土)Windowsエクスプローラで文字化けしないFTPサーバ

2018/12/08 23:55 PC(Linux)
■前提
クライアント: Windows10
サーバ: Linux

当初vsftpdでFTPサーバを運用していたが、Windowsからアップロードした際に日本語ファイル名が文字化けしたと連絡が来た。自分でも確認してみたが、確かにWindowsのエクスプローラからアップロードすると文字化けする。

文字化けの挙動が不可解。全文字化けではなさそう。
でもダメ文字ではなく、特定の文字位置が化けているような…

今回時間がなかったので細かい調査は実施せず、とりあえず解消方法だけ模索した。

原因として一番怪しいのは、Windowsの下記仕様変更だ。

Winows10でのFTPコマンド実行時の振る舞いについて

FTP接続の頭で、
OPTS UTF8 ON
する。その事自体は問題ない。
のだが、この後の「Windows側でファイル名をUTF-8にエンコード」のときにバグって化けてるとしか思えない。
つまり、OPTSが通ってしまうとUTF-8転送になってバグルートに落ちるので、OPTSを拒否させた上でSJISで転送してもらわないといけない。

vsftpdだとOPTSを無視することはできるが、クライアントとサーバで文字コードを変換させる設定が見つからなかった。

変換無しで考えた場合……
サーバ側UTF-8ファイル名だとWindowsからだと文字化けしていそう。
サーバ側SJISファイル名だと多分UTF-8解釈させると思うので、ちゃんとしたFTPクライアントから文字化けしていそう。

仕方ないので、vsftpdからproftpdに切り替えた。
proftpdにはエンコーディングをサーバ・クライアントで変換する設定がある。
proftpd.confに下記設定で、とりあえずWindowsエクスプローラからのFTPで文字化けがなくなった。
UseEncoding UTF-8 CP932 strict
LangDefault en_US
LangDefault en_US入れないとメッセージが文字化け。
クライアントがFFFTP、WinSCPあたりでも問題なさそう。とりあえずこれで。

[参考]
IEからProFTPdにアクセスするとファイル名が文字化けする - ブログ - ワルブリックス株式会社

あと恒例の文字化けファイルをLinux上でいじる方法。
Linuxでファイルが文字化けした際の対処法 | OXY NOTES

バッドノウハウ的なので追求しない。

2018/11/23(金)PinterestでPINした画像をGoogleドライブに保存できるようにする

■やりたいこと
PinterestでPINした画像をローカルで閲覧したい。おおむね、お絵描きの参考用途。
・ブラウザ窓だと参考画像として扱いづらい
・貧弱な通信環境でも閲覧できる
・いちいちブラウザ開くのが面倒

やっぱりローカルでアクセスできた方が便利だよね。

■調査と課題
直接Pinterestからデータを引っ張ってくることもできるが、定期実行なり待受の仕組みを立てる必要がある。可用性を高く、一方でできるだけ手間や予算がかからない方法を検討したい。自前サーバがあるのでこれを使ってももちろん良いのだが、可用性であればやはりクラウドに軍配が上がる。(半ば興味本位で)極力クラウドサービスだけで構成することを目指す。

ストレージサービスは一般にローカル同期の仕組みを持っているため、Pinterestからストレージサービスに連携できれば要求が満たせそう。

Pinterestとローカルサービスを繋ぐ方法として、IFTTTなどの連携機能構築サービスが有る。まずはこれを調査した。おおむね、パーソナル用途で機能と使いやすさを両立しているのは2018/10現在下記3つくらい。

・IFTTT(Maker)
・Microsoft Flow
・Integromat

これら全てに、Pinterestインプット・Googleドライブアウトプットアダプタがあった。
なのでこれらを繋げばいいだけに思える。

しかし、いざ使ってみると画像ではなくhtmlファイルが連携される。
どれもPinterestが公式で提供したアダプタらしく、画像そのものを外部サービスに渡さない意図的な作りと思われる。

ちなみに、各サービスの比較としては、
▼機能性
Microsoft Flow > Integromat > IFTTT

▼簡便性
IFTTT > Integromat > Microsoft Flow

高機能な方が一見良さそうだが、この手のクリックアンドポイントロジック構築は高機能にするほど最終的にフローチャート作成サービスになってしまい見通しやメンテナンス性が怪しくなるという特性がある。
複雑なものを作ろうとするほど、どこかの境界でコードを自分で書いたほうが俯瞰性が良くなる。

IFTTTはINPUT(THIS)とOUTPUT(THAT)の接続だけしか選べないが、これくらいがちょうど良いように思う。見通しが悪くならない範囲で足すなら、出力を多数化(3つまでとか)、フィルタを一段、データ加工を簡単なもので1段くらいだろうか(その場合IFTTT以外のサービスないしMakerを選ぶことになる)。

いずれのサービスもコール回数に制限がある。
今回用途では1日10枚以内が目処。
上記に挙げた3つのフリープランでいずれも問題なさそうだった。

将来増えるか減るかなどの見通しは不明。

さて、結局以下のように構成した。

■実装
IFTTT⇒Google Apps Script(GAS)⇒Googleドライブ⇒ローカル(Googleドライブの連携機能)

・IFTTTからpinterestのurlをGASに渡す
・GAS内で、FetchしてPinterestのhtmlを取得
・簡単な正規表現マッチで画像URLおよび属性情報を取得
・画像をFetchしてGoogleドライブへ保存
・ファイル名はID+PINの詳細があればそれをファイル名とする
・第一階層のボード名をフォルダとしてそこに保存する

1) GASで下記スクリプトを作成。作成時に各種認証を要求されるので設定してやる。
function doPost(e) {
  // パラメータの取得
  var jsonString = e.postData.getDataAsString();
  var data = JSON.parse(jsonString);
  var pinUrl = data.pinUrl;
  var boardName = data.boardName;
  
  // フォルダ無ければ作成  
  var rootFolder = DriveApp.getRootFolder();
  var pinterestFolder = null;
  var targetFolder = null;
  var folderIterator = rootFolder.getFoldersByName('pinterest');

  if (folderIterator.hasNext()) {
    pinterestFolder = folderIterator.next();
  } else {
    pinterestFolder = rootDir.createFolder('pinterest');
  }

  targetFolder = pinterestFolder;

  // ボード名が取れていればサブフォルダとしてボードフォルダを作成
  if (boardName) {
    folderIterator = pinterestFolder.getFoldersByName(boardName);
    
    if (folderIterator.hasNext()) {
      targetFolder = folderIterator.next();
    } else {
      targetFolder = pinterestFolder.createFolder(boardName);
    }
  }
  
  // html解析処理
  var pinResponseText = UrlFetchApp.fetch(pinUrl).getContentText();
  var imageUrl = pinResponseText.match(/property="og:image".*content="([^"]*)"/)[1];
  // 128文字まで
  var description = pinResponseText.match(/img alt="([^"]*)"/)[1].substring(0, 128);
  
  var imageFilename = pinUrl.match(/[^\/]*$/)[0];
  
  // 画像取得処理
  var imageResponse = UrlFetchApp.fetch(imageUrl);

  // 拡張子は画像本体のContent-Typeから作成  
  var imageFilenameExt = imageResponse.getHeaders()["Content-Type"].match(/image\/([a-zA-Z]*)/)[1];
  if (imageFilenameExt == 'jpeg') {
    imageFilenameExt = 'jpg';
  }
  
  var imageBlob = imageResponse.getBlob().setName(imageFilename + ' - ' + description + '.' + imageFilenameExt);
  
  targetFolder.createFile(imageBlob);
  
}
IFTTTから呼び出せるようにWebアプリケーションとして公開してやる。


2) IFTTTで、Pinterestのオフィシャルアダプタを使いWebHookでGASに連携する。設定は以下。

▼THIS(INPUT)
Pinterest公式の"New Pin on your board"
Pick a board: "Any Board"

▼THAT(OUTPUT)
IFTTT公式の"Make a web request"
URL: https://script.google.com/macros/s/****/exec
Method: POST
Content Type (optional): application/json
Body (optional):
{"pinUrl":"{{PinURL}}", "boardName": "{{Board}}"}
以上で完成。かなりお手軽。

■まとめと使い勝手について
クラウド、フリープラン、更には認証情報をOAuthの世界で完結させたPinterest画像のローカル保存サービスが構築できた。
目標は達成でき、そこそこ便利に使っている。
ただ、PinterestからIFTTTへの発火に少しラグが有り、Googleドライブへの連携にもラグがあるためあまり即時連携という感じにはならない。

特性を理解して使用しつつ、どうしても即時で必要な場合はChromeの拡張などを利用してダウンロードすることにする。

■IFTTTについて
便利。機能を絞っているところが良い。
もちろん絞ってる分できないことがでてくる。
直感的に苦手なことはわかるので、あとは自分が使う上でどういったところでIFTTTで完結できないのか何を組み合わせればよいのかを今後確認していく。

■Google App Script(GAS)について
ロジックを作る必要があり、アウトプットがGoogle関係であればこれ一択。
Javascriptで大体何でもできるし、ドキュメントの必要十分さ、特に開発環境の素晴らしさはワンダフル。

現状無料で使えてるが、将来的に有料化or閉鎖しないかはちょっと心配。あとは制限。
作るときはフェッチの制限を忘れていたがやっぱりあるよね。
Google_Apps_Scriptの無料制限メモ
今回の内容だとURL Fetch 100MBが制限になりそう。
この仕組に限定すれば数百枚くらいは耐えるだろう。

AWSで相当するのは、Amazon API Gateway + Lambdaだろうか。
こいつらは本番運用には良いが、作って動かすまでのハードルが低くない。
こういったミニサービスや、使い捨てのテストAPIなどを作りたい場合GASのほうが向くように思う。


■今後の課題
サービスの選択・組み合わせを継続的に知識アプデするのと、GASがかなりawesomeだったのでできることを覚えたい。


■結論
最近のグルーサービスすげえね

2018/11/02(金)しょぼいカレンダーのチャンネル名変更

2018/11/02 16:24 PC(全般)
2018/10/01で変更されていた模様。

https://jbbs.shitaraba.net/bbs/read.cgi/anime/3083/1317273356/61-63
「BS Japan」→「BSテレ東」
「BS11デジタル」→「BS11イレブン」
「TwellV」→「BS12トゥエルビ」
「FRESH!」→「FRESH LIVE」
チャンネル名縛りで動くサービス・ソフトが動かなくなっていたので注意。

自作のtwitter連携は大丈夫だったっぽい。
OK キャンセル 確認 その他