2022年3月9日水曜日

jQuery を使ってファイルをアップロードする方法

jQuery を使ってファイルをアップロードする方法

概要

前回はライブラリを使った方法を紹介しました
今回は jQuery のみを使ってファイルをアップロードする方法を紹介します

環境

  • macOS 11.6.4
  • Ruby 3.0.3p157
  • sinatra 2.2.0

サーバ側実装

サーバ側のコードは前回と同じです

クライアント側実装

  • vim views/index.erb
<!DOCTYPE HTML>
<html>
  <head>
  <meta charset="utf-8">
  <title>Upload Test</title>
  </head>
<body>
<input id="upload_file" type="file" name="file">
<button id="upload" type="button">Upload</button>
<div id="result"></div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script>
$(function () {
  $('#upload').click(function() {
    let fd = new FormData();
    fd.append("file", $('#upload_file').prop('files')[0]);
    $.ajax({
      url:'/upload',
      type:'post',
      data: fd,
      processData: false,
      contentType: false,
      cache: false,
    }).done(function (data) {
      $('#result').text("uploaded.")
    }).fail(function() {
      $('#result').text("upload failed.")
    });
  });
});
</script>
</body> 
</html>

少し解説

jQuery でファイルを送信するには FormData + ajax を使います
ファイルは複数指定されることを想定していますが今回は1ファイルのみ指定可能にしているので先頭のファイルのみを送信します ($('#upload_file').prop('files')[0])

prop のあとの files は必ず「files」でなければなりません

あとはサーバ側でファイルを保存すれば OK です

最後に

ライブラリを使うとプログレスバーやアップロード後のコールバックメソッドが予め定義されているので便利なのは便利なのですが使わない機能もあります

本当に簡単なフォームを作成したいだけだればこれくらいでもいいかもしれません

参考サイト

2022年3月8日火曜日

jQuery-File-Upload を使ってみた

jQuery-File-Upload を使ってみた

概要

jQuery-File-Upload は jQuery を使って簡単にファイルアップロード用のフォームが実装できるライブラリです
今回は簡単な使い方を紹介します

なおこのリポジトリはすでにアーカイブ状態になっているので使用する際はご注意ください

環境

  • macOS 11.6.4
  • Ruby 3.0.3p157
  • sinatra 2.2.0

サーバ側実装

  • bundle init
  • vim Gemfile
gem "sinatra"
gem "thin"
  • bundle install
  • mkdir views public upload
  • vim app.rb
# coding: utf-8
require 'sinatra'

class TestUpload < Sinatra::Base
  get '/' do
    erb :index
  end

  post '/upload' do
    if params[:file]
      save_path = "./upload/#{params[:file][:filename]}"
      File.open(save_path, 'wb') do |f|
        f.write params[:file][:tempfile].read
        @msg = params[:file][:filename].force_encoding("utf-8")
      end
    else
      @msg = "Upload Error..."
    end
    content_type :json, :charset => 'utf-8'
    {'filename': @msg}.to_json
  end
end

クライアント側実装

  • vim views/index.erb
<!DOCTYPE HTML>
<html>
  <head>
  <meta charset="utf-8">
  <title>Upload Test</title>
  </head>
<body>
<input id="fileupload" type="file" name="file" data-url="/upload">
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="jQuery-File-Upload/js/vendor/jquery.ui.widget.js"></script>
<script src="jQuery-File-Upload/js/jquery.iframe-transport.js"></script>
<script src="jQuery-File-Upload/js/jquery.fileupload.js"></script>
<script>
$(function () {
  $('#fileupload').fileupload({
    dataType: 'json',
    add: function (e, data) {
      data.context = $('<button></button>').text('Upload')
      .appendTo(document.body)
      .click(function () {
        data.context = $('<p></p>').text('Uploading...').replaceAll($(this));
        data.submit();
      });
    },
    done: function (e, data) {
      data.context.text(data.result.filename);
    }
  });
});
</script>
</body> 
</html>

jQuery-File-Upload のインストール

CDN などですべてのファイルが公開されているわけではないので手元に持ってきてから使用します

動作確認

フォーム画面が表示されてファイルが upload ディレクトリ配下に作成されれば OK です

少し解説

$('#fileupload').fileupload を呼び出すだけで必要なフォームを作成してくれます
今回はボタンを押したらアップロードしたかったので add コールバックメソッドを実装していますがなくても動作します

レスポンスは JSON 形式を想定しているので Sinatra から返却されるレスポンスは application/json にする必要があります

Sinatra 側ではファイルをローカルに保存していますがここは自由に変更して OK です

Sinatra の public ファイルは public ディレクトリ配下に配置する必要があるので git clone したライブラリは public 配下に配置しています

最後に

スタイルなど当てることもできるようです
リポジトリはすでにリードオンリーになっているのでくれぐれも使用する際は注意しましょう

参考サイト

2022年3月7日月曜日

git でローカルにあるブランチをすべて削除する方法

git でローカルにあるブランチをすべて削除する方法

コマンド

  • git branch | grep -v \* | xargs git branch -D

マージ前のブランチもすべて削除されます
マージ前のブランチを残したい場合は git branch --merge オプションで実行します

2022年3月4日金曜日

botocore でプロキシを設定する方法

botocore でプロキシを設定する方法

概要

boto3 を使って s3 API にプロキシ経由でアクセスする方法を紹介します

環境

  • Ubuntu 18.04
  • Python 3.10.2
  • boto3 1.12.11

サンプルコード

import boto3
from botocore.config import Config


class TestClient():

    def __init__(self):
        proxies = {
            'http': 'http://192.168.100.1:3128',
            'https': 'http://192.168.100.1:3128'
        }
        config = Config(
            region_name='us-east-1',
            proxies=proxies
        )
        self.client = boto3.client(
            's3',
            aws_access_key_id='AKIxxxxxxxxxxx',
            aws_secret_access_key='xxxxxxxxxxx',
            config=config,
        )

    def list_buckets(self):
        return self.client.list_buckets()


if __name__ == '__main__':
    cli = TestClient()
    print(cli.list_buckets())

botocore.config を使って設定できます
各種設定は環境変数やコンフィグファイル化してください

参考サイト

2022年3月3日木曜日

pyjwt で jwk を使って認証情報を取得しそこから jwt を復号化する方法

pyjwt で jwk を使って認証情報を取得しそこから jwt を復号化する方法

概要

jwk は鍵を公開鍵管理する API で JSON 形式で取得できます
今回は jwk で取得した鍵情報を使用してそこから jwt を復号化する方法を紹介します
なお暗号化方式は ES512 で楕円曲線演算方法は P-521 の場合になります

鍵などの情報は適当に記載しています

環境

  • macOS 11.6.4
  • Python 3.10.2
  • pyjwt 2.3.0

サンプルコード

import jwt
import json

from jwt.algorithms import ECAlgorithm
from cryptography.hazmat.primitives import serialization

jwks = '{"keys":[{"alg":"ES512","crv":"P-521","kid":"key_id1","kty":"EC","use":"sig","x":"66_bytes_characters","y":"65_or_66_bytes_characters"}]}'
jwks_dict = json.loads(jwks)
jwt_token = "encripted_json_web_token"

# トークンから kid や alg などのヘッダ情報を取得
header = jwt.get_unverified_header(jwt_token)
# print(header)

# 公開鍵の一覧から kid に一致する jwk を取得
jwk_json = [jwk for jwk in jwks_dict['keys'] if jwk['kid'] == header['kid']]

# 公開鍵情報から暗号化するための pem キーを生成
public_key = ECAlgorithm.from_jwk(jwk_json[0])
pem = public_key.public_bytes(encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo)

# pem キーを使って jwt トークンを復号化する
decoded_jwt = jwt.decode(jwt_token, pem, algorithms=jwk_json[0]['alg'])
print(decoded_jwt)

軽く説明

まず jwk が公開してる鍵情報の JSON を jwks に設定しています
鍵が複数ある場合もあるので今回は複数の鍵を想定しています

まずトークン情報から jwt.get_unverified_header を使ってヘッダ情報を取得します
ここに暗号化した鍵の kid やアルゴリズムが記載されています

次に kid を元に公開鍵の一覧から復号化するための jwk を取得します
ヘッダに含まれる kid を使って合致する公開鍵を設定します

取得した jwk から PEM キーを生成します
生成するには ECAlgorithm.from_jwk を使います

あとは jwt.decode を使ってトークンをデコードします

pyjwt にパッチを当てる必要がある

実は pyjwt をそのまま使った場合だと動作しない場合があります
jwk で公開している楕円曲線演算方法が P-521 の場合 Y の鍵が 66 bytes である必要があります
しかし実際は 65 or 64 bytes (64bytes である可能性は 0.2%) になる可能性があり pyjwt では 65 or 64 bytes のときを考慮しておりません

なので以下のように条件を変更するパッチを当てる必要があります

  • vim /path/to/venv/site-packages/jwt/algorithms.py
elif curve == "P-521":
    if len(x) == 66 and (len(y) == 66 or len(y) == 65):
        curve_obj = ec.SECP521R1()
    else:
        raise InvalidKeyError("Coords should be 66 bytes for curve P-521")

最後に

jwk は基本的に https 経由で取得することになると思います
jwt も REST などの API 経由のレスポンスで取得することになると思います

pyjwt は修正が必要なので別の jwt ライブラリを使えばパッチを必要はないかもしれません (jose)

参考サイト

2022年3月2日水曜日

Python で jwt を試してみた

Python で jwt を試してみた

概要

過去に Ruby で jwt で試しました
今回は Python で試してみました

準備

  • pipenv install pyjwt

サンプルコード

import jwt

# 文字列 -> json web token
encoded_jwt = jwt.encode({"some": "payload"}, "secret", algorithm="HS256")
print(encoded_jwt)

# json web token -> 文字列
decoded_jwt = jwt.decode(encoded_jwt, "secret", algorithms=["HS256"])
print(decoded_jwt)

鍵を使った場合

別途 cryptography が必要になります

  • pipenv install cryptography

pem 形式の鍵をテスト用に作成します

  • ssh-keygen -t rsa -b 4096 -m PEM -f id_rsa
  • openssl rsa -in id_rsa -pubout -outform PEM -out id_rsa.pub
import jwt

priv_key_path = "id_rsa"
pub_key_path = "id_rsa.pub"
encoded_jwt = ""

with open(priv_key_path) as private_key:
    encoded_jwt = jwt.encode({"some": "payload"}, private_key.read(), algorithm="RS256")
    print(encoded_jwt)

with open(pub_key_path) as public_key:
    decoded_jwt = jwt.decode(encoded_jwt, public_key.read(), algorithms=["RS256"])
    print(decoded_jwt)

最後に

簡単に使えます
その他オプションでヘッダなども付与することができます

参考サイト

2022年3月1日火曜日

GCP のクラウドストレージでバケットの使用量を確認する方法

GCP のクラウドストレージでバケットの使用量を確認する方法

概要

なぜかクラウドストレージのメニューでは確認できないようです

以下2つの方法を紹介します

環境

  • macOS 11.6.4
  • gsutil 5.6

gsutil を使う

  • gsutil ls -l gs://your.backet.name/

メトリックエクスプローラを使う

Resource & Metric で GCS BUCKET から TOTAL BYTES を選択することで見れます

最後に

普通にバケットのエクスプローラ画面で確認できるようにしてほしい気もします

API の設計上数値的な情報は全部メトリックに集めようという思想なんだと思います