D&Dやコピペでファイルアップロードできるtextareaをサクッと作る

GithubやQiitaなんかで使えるような、使い勝手の良い画像やファイル添付のTextareaをサクッと作った

使ったライブラリは Inline Attachement。 今回はレガシーWebAppに使ったので、JQueryで実装。

設定はドキュメントに掲載されているが、実はここに書かれている以上に拡張性があるので、コード自体を読むことをおすすめする。これによりやりたかったことは全て実現できた。

やりたかったこと

どんなファイルでも添付できるようにしたい

デフォルトだと画像ファイルに制限されているが、どんなファイルでも受け付けるようにしたかった。 コードを読んで、allowedTypes: ['*'] とすれば良いことがわかったのでそのようにした。

アップロードされたファイルの名前が、サーバサイドでわかるようにしたい

デフォルトだと、サーバサイドから見えるファイル名は、image-タイムスタンプ.拡張子となり、もとのファイル名がわからない。 ドキュメントに記載は無いが、remoteFilenameというプロパティに関数を入れてやると、引数にファイルオブジェクトを渡してくれるので、ファイルオブジェクトの内容を使ってファイル名を作成して返してやることで、サーバサイド側ではそのファイル名で見える。 例えば下記のようにすると、もともとのD&Dされたファイル名がサーバサイドに送付される。

    remoteFilename: (file)=>{return file.name}

ファイルの種類によって、埋め込まれるコードを変えたい

Markdown形式で入力するテキストエリアだったので、ファイルの種類によって埋め込まれるコードを修正したかった。

![image.png](url to image)   ←画像ならこっち
[document.docx](url to file)  ←ドキュメントならこっち

埋め込まれるコードは、urlTextプロパティで静的に設定できるが、このプロパティに関数を与えることで好きなデータに編集できる。 ファイルの種類はサーバサイドで判定したMIMEJSONに入れて返し、これを読んで画像ファイルか普通のファイルかどうかの判別がつくようにしてある。 また、リンク文字列ももともとのファイル名にしたかったので、同じくサーバサイドからオリジナルのファイル名を返して埋め込むようにしている。

    urlText: (filename,result)=>{
      if(result.mime.match(/image/)) {
        return `![${result.orig_file_name}](${filename})`
      } else {
        return `[${result.orig_file_name}](${filename})`
      }
    }

マウスオーバしたときにドロップできる感じを出す

ファイルをドラッグしてきたときに、textareaに点線で枠線を表示することで、ドロップできますよー感を出した。

textarea.dragover {
    border: dashed 6px #ccc;
    padding: 12px 6px 0px;
}
  $('textarea').on('dragenter dragover', function() {
    $(this).addClass('dragover')
  })
  $('textarea').on('drop dragleave dragend', function() {
    $(this).removeClass('dragover')
  })

最終型

JSはこんな感じ。

$(function(){
  // ファイル貼り付け
  $('textarea').inlineattachment({
    uploadUrl: 'upload_attachment.php',
    allowedTypes: ['*'],
    remoteFilename: (file)=>{return file.name},
    urlText: (filename,result)=>{
      if(result.mime.match(/image/)) {
        return `![${result.orig_file_name}](${filename})`
      } else {
        return `[${result.orig_file_name}](${filename})`
      }
    }
  });
  $('textarea').on('dragenter dragover', function() {
    $(this).addClass('dragover')
  })
  $('textarea').on('drop dragleave dragend', function() {
    $(this).removeClass('dragover')
  })
});

CSSはこんなん。

textarea.dragover {
    border: dashed 6px #ccc;
    padding: 12px 6px 0px;
}

PHPはこんなもんで。

$response = array();
$upload_directory = "/data/";
if (isset($_FILES['file'])) {
    $file = $_FILES['file'];
 
    // MIMEタイプの取得
    $finfo = new finfo(FILEINFO_MIME_TYPE);
    $mime_type = $finfo->file($file['tmp_name']);
 
    // ファイルのID(=MD5ハッシュ)を計算
    $file_md5 = md5_file($file['tmp_name']);
 
    // ファイルの移動
    move_uploaded_file($file['tmp_name'], $upload_directory . "/" . $file_md5);
 
    $response['filename'] = "file_get.php?id=".$file_md5;
    $response["mime"] = $mime_type;
    $response["orig_file_name"] = $file["name"];
} else {
    $response['error'] = 'Error while uploading file';
}
echo json_encode($response);