JavaScript
InternetExplorer
ActiveX

神InternetExplorerでバイナリファイルの書き込み

こんにちわ。

皆さんは業務でInternetExplorerはお使いでしょうか。
弊社では、残念ながら未だIE11が社内標準として使われ続けています。

ES6が全滅な時点で、個人的には極力使いたくない負の遺産ですが...

文句を言っていても始まらないので、こうなったら開き直って使い倒してやろうと思います。

IEと言えばActiveX

ご存知、WindowsにベッタリのAPIです。

開き直って使ってみて思ったのですが、本当に何でも出来るんですね。

// CMDオブジェクト呼出
const msWsh = new ActiveXObject("WScript.Shell");

// コマンド実行(同期)
msWsh.Run("foo", 0, true);

これでコマンドプロンプト打ち放題ですよ。

// FSオブジェクト呼出
const msFso = new ActiveXObject("Scripting.FileSystemObject");

// ファイル指定
const file = msFso.OpenTextFile("path", 2, true, -1);

// テキスト書込
file.Write("bar");

任意のディレクトリへファイル保存も簡単に出来ます。

ブラウザ側で実行防止設定があるとは言え、セキュリティとは一体...

バイナリファイルを読み書き

さて本題。
テキストファイルは簡単に出来るんですけどね、バイナリファイルは小細工が必要です。

// ActiveXインスタンス用変数の確保
let msFso;
let msWsh;

// IE用
if(/trident/.exec(navigator.userAgent.toLowerCase())){
    msFso = new ActiveXObject("Scripting.FileSystemObject");
    msWsh = new ActiveXObject("WScript.Shell");
}

// ActiveXプロパティ
const msActiveX = {

    // 引数: コマンド文字列
    cmd: function(command){
        return msWsh.Run(command, 0, true);
    },

    // 引数: base64文字列, 出力ファイル名
    writer: function(base64, path){

        // msFso.GetSpecialFolder()は特殊ファイルパス取得メソッド
        // 2 => %temp%
        const temp = msFso.GetSpecialFolder(2) + "\\temp.tmp";

        // ファイルオブジェクト
        const file = msFso.OpenTextFile(temp, 2, true, -1);

        // base64書込
        file.Write(base64);
        file.Close();

        // base64 => binary
        this.cmd("certutil -f -decode " + temp + " " + path);
    }
}
HowToUse
const reader = new FileReader();
reader.onload = function(){
    msActiveX.writer(new String(reader.result).replace(/^.*?base64,/, ""), "c:\\myFile.zip");
}
reader.readAsDataURL(myBlob);

FSOでの読み書きは文字列しか出来ないので、一旦base64で保存する所がミソです。

後は、WSHでWindows標準搭載のcertutilコマンドを叩いてbase64 => binaryに変換すれば完成です。

実業務で役立ちそうなオマケ

ブラウザ上で生成したファイルを纏めてOutlookに添付するアレ。
(やりたくないけど仕事でよくやるやつ)

JSZip.jsを使うと、良い感じに出来ます。
Encoding.jsを使うと、CSVなどを生成した場合、神Excelで読込可能なSJISに変換出来ます。

let msOutlook;

if(/trident/.exec(navigator.userAgent.toLowerCase())){
    msOutlook = new ActiveXObject("Outlook.Application");
}

const msActiveX = {

    // 引数: 添付ファイルパス
    mailer: function(path){

        // メールオブジェクト
        const mail = msOutlook.CreateItem(0);

        // ウィンドウを表示
        mail.Display();
        mail.GetInspector.WindowState = 2;

        // 本文の設定
        mail.To = "myaddr@example.jp";
        mail.Subject = "の件につきまして";
        mail.Body = "何卒宜しくお願い致します。";

        // 添付ファイル指定
        mail.Attachments.Add(path);
    }
}
HowToUse
//JSZip.jsとEncoding.jsを使った例

function toSjis(text){
    return Encoding.convert(text, {
        to: "SJIS",
        from: "AUTO",
        type: "array"
    });
}
const csvArray = toSjis(myTextUtf16);

const jszip = new JSZip();
jszip.file("申請書1.csv", csvArray, {binary: true});

jszip.generateAsync({type: "base64"})
    .then(function(body){
        const attach = "c:\\myFile.zip";
        msActiveX.writer(body, attach);
        msActiveX.mailer(attach);
    })
    .catch(function(error){
        return new Error(error);
    })

もう平成も終わろうとしているのに、こんなパワープレイがまかり通ってるのはおかしいよ!!!

おわりに

とても悔しいけどWindows使ってる身からするとActiveX便利ですね...
SJISと神Excelは滅べ