at backyard

Color my life with the chaos of trouble.

様々な言語からMinioのファイルアップロードを試す

様々な言語からMinioを試してみる。
※といっても、現在は3言語しかまだ書いていない

f:id:shinshin86:20170318082954p:plain

最近 Go言語で書かれた、S3互換のオブジェクトストレージサーバを触り始めたので、
各言語での試し方をこちらにメモしておく。
なお、あくまで現時点でのやり方となるので、もしこの記事が古くなっているようだったら、
公式からDocsに飛んで確認するのが望ましい。

www.minio.io



まずはNode.jsから

Node.js

セットアップ

npm install minio

実装

const Minio = require('minio');
const fs = require('fs');
const path = require('path');

const location = 'us-east-1';

const minioClient = new Minio.Client({
  endPoint: END_POINT,
  port: PORT,
  secure: false, // テストなのでひとまずfalseにしている
  accessKey: ACCESS_KEY,
  secretKey: SECRET_KEY
});

// Bucket作成
function createBucket(bucketName) {
  minioClient.makeBucket(bucketName, location, function(err) {
    if(err) return console.log(err);

    console.log('Bucket created successfully in ', location);
    console.log('Created bucket name  => ', bucketName);
  });
}

// imagesディレクトリ内の画像ファイルを作成したBucketに全てアップロードする
function fileUpload(bucketName, uploadDir) {
  let files = fs.readdirSync(uploadDir);
  files.forEach(function(file) {
    console.log('Upload file =>', file);

    // アップロードするファイル名
    let filePath = path.join(uploadDir, file);
    // fPutObjectを使用してファイルをBucket "SampleImages"にアップロードします
    minioClient.fPutObject(bucketName, file, filePath, 'image/png', function(err, etag) {
      if(err) return console.log(err);
      console.log('File upload successfully!!');
      console.log('Uploaded File Name => ', file);
    });
  });
}

// 作成するBucket名
const bucketName = 'nodetest';

// アップロード対象が格納されたディレクトリ名
let targetDir = 'images';

// アップロード対象の絶対パス
let uploadDir = path.join(__dirname, targetDir);

// 実行
createBucket(bucketName);
fileUpload(bucketName, uploadDir);

Python

セットアップ

pip install minio

実装

from minio import Minio
from minio.error import ResponseError
import os
import glob

# テストなのでひとまずsecureはFalseにしている
minioClient = Minio(END_POINT,
    access_key=ACCESS_KEY,
    secret_key=SECRET_KEY,
    secure=False)

# Bucketを作成する
def create_bucket(bucket_name):
    try:
        minioClient.make_bucket(bucket_name, location='us-east-1')
    except ResponseError as err:
        print(err)

# 対象ディレクトリなのファイルを取得し、アップロードする
def file_upload(bucket_name, upload_dir):
    for i in glob.glob(os.path.join(upload_dir, '*.png')):
        try:
            minioClient.fput_object(bucket_name, os.path.basename(i), i)
        except ResponseError as err:
            print(err)


# 作成するBucket名
bucket_name = 'pythontest'

# アップロード対象のファイルが格納されたディレクトリ名
target_dir = 'images'

# アップロード対象の絶対パス
upload_dir = os.path.join(os.getcwd(), target_dir)

print('File upload start');
create_bucket(bucket_name)
file_upload(bucket_name, upload_dir)
print('File upload successfully!')

Go

セットアップ

go get -u github.com/minio/minio-go

実装

package main

import (
	"fmt"
	"github.com/minio/minio-go" // Import Minio library.
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
)

func main() {
	bname := "gotest"
	tdir := "images"
	tpath, err := os.Getwd()
	ssl := false // テストなのでひとまずfalseとしている
        location := "us-east-1"

	minioClient, err := minio.New(Endpoint,
		AccessKey,
		SecretKey, ssl)

	if err != nil {
		log.Fatalln(err)
	}

	// bucketを作成
	err = minioClient.MakeBucket(bname, location)
	if err != nil {
		// 既に同名のbucketが存在していないかを確認する
		exists, err := minioClient.BucketExists(bname)
		if err == nil && exists {
			fmt.Printf("We already own %s\n", bname)
		} else {
			log.Fatalln(err)
		}
	} else {
		fmt.Printf("Successfully created %s\n", bname)
	}

	// 指定したディレクトリからファイルを取得
	filelist, err := ioutil.ReadDir(filepath.Join(tpath, tdir))

	if err != nil {
		fmt.Println(err)
		return
	}

    // 取得したファイルをすべてアップロードする
	for index, element := range filelist {
		fmt.Printf("Uploading... %s : %d\n", element.Name(), index)

		n, err := minioClient.FPutObject(bname, element.Name(), filepath.Join(tpath, tdir, element.Name()), "image/png")
		if err != nil {
			log.Fatalln(err)
		}
		fmt.Printf("Successfully uploadefd %s of size %d\n", element.Name(), n)
	}
}

ひとまずここまで。
汚いコードをただ書き散らしているような気もする。。。精進していかねば。

あと、昨日からElixir触り始めたので、Elixirのサンプルも近々書きたい。


ちょっと試すだけでも使用コストがかかってしまうAmazon S3の代替手段として
Minioの存在価値はとてもあると思う。気軽にローカルでの開発などに使えるので、とても便利。

インストール関連の記事は検索してみると色々出て来るし、公式のスタートガイドも分かりやすい。

いいですね、Minio

Ruby on Rails 5でページ遷移時にJavaScriptが読み込まれない問題の解決法

個人度がいつも以上に高い備忘録なので、この内容がリーチしない方にとっては読む意味ありません。

表題の件で悩まれている方がこのページに訪れた際は、下に貼ったリンク先の記事が解決の手立てとなるかもしれませんので、ぜひ見てみてください。
(現に私が解決したので)


Rails 5でアプリ書いているとき、ページ遷移時にJavascriptが読み込まれなくて困っていた。
解決のきっかけになったのは、下記の記事

qiita.com


最初、下記のように書いていたのだけど、これが間違えていた。

$(document).on 'ready page:load', ->
  console.log("call function!")

上に貼った記事にもある通り、この書き方ではRails 5に対応していないようだ。
正しくは下記。

$(document).on 'turbolinks:load', ->
  console.log("call function!")

感謝!

Node.js v 0.10系で、Promiseなどを使わないで、無理矢理に同期処理を行う。

JavaScript備忘録記事。

今年に入ってからNode.js + MongoDB(with mongoose)という組み合わせで仕事をしている。
ただ、プロジェクトの諸事情により、最近までNode.jsはPromiseが使えない古いバージョンを使わざる負えない状況だった。
具体的には0.10系。
そうなると困るのが同期的な処理をしたい時。

例えば"mongoose"を使って、指定した値を取得し処理をしたい時などに困るシーンが出てくる。
具体的には下記のような泥臭い処理を実装して凌いでいた。

var userModel = dataModel.getUserModel(); // "user"コレクションのMpdel取処理とする

// userコレクションから"country"が"us"のデータを取得する
userModel.find({"country" : "us"}, function(err, users) {

  // エラー処理
  if(err) {
    errorHandling();
  }

  // 取得件数が0件の場合の処理
  if(users.length === 0) {
    notGetProcessing();
  }

  // global変数として持たせることで共通的なカウンターとして使用
  counter = users.length;
  console.log("find user count : ", counter;

  users.forEach(function(user) {
    // 取得したデータに対する処理。この場合、"Processing"に処理を移し、
	// そちらでの処理が完了した後にGlobal変数である"counter"を-1していく
    setTimeout(function(a) {
      processing(a);
    },1000, user);
  });

  // Global変数である"counter"が"0"になった時点で、
  // 取得データに対する処理が全て完了したものとし、終了関数に処理を渡す
  setInterval(function() {
    if(counter === 0) {
      // End processing
      endProcess();
    }
  }, 100);
});

相当なスパゲティコードである。茹ですぎて完全に伸びている状態といったところか。
このように泥臭い処理をいたるところでしていたので、
相当コードがスパゲティになっていて、保守コストも高く付きそうになってしまっていた。
が、ある時点でNodeのバージョンが上がることになり、今ではバージョン6系で書いている。

Promiseも使えるようになったので、おかげで下記のように処理を変更できる。
(青臭いコードかもしれない。もし、もっとこうした方がスマートに出来るなどありましたら、コメントいただけると幸いです。。。)

// "country"が"us"のuserデータをPromiseで取得
function getUsUser() {
  let userModel = dataModel.getUserModel();
  return userModel.find({"country" : "us"}).exec();
}

// メイン処理
function main() {
  let promise = getUsUser();
  promise.then(function(users) {

    // 取得件数が0件の場合の処理
    if(users.length === 0) {
      notGetProcessing();
    }

    console.log("find user count: ", users.length);

    // 取得した全データに対して処理を行う
    users.forEach(function(user) {
      processing(user);
    });

    return users.length;
  })
  .then(function(userCount) {
    console.log("Successful user count : ", userCount);

    // 終了処理
    endProcess();
  })
  .catch(function(err) {
    conosole.log(err);

    // 終了処理
    endProcess();
  });
}

// 実行関数
main();

毎日泥臭いコードを書いていたのは大変だったが、
いかにして無理やり同期的に処理させるか?と考えてコードをガリガリ書いていたのは、
それはそれでとても勉強になったと感じている。

これからの「正義」の話とイマヌエル・カントの純粋理性批判

今日は本に関する投稿をします。
なぜ急に本かというと、うちの会社の社長が先日、突然自身の読んだ本の感想文を全社員宛に共有したのだが、
それを読んでみたところかなり面白く、他人の読書感想文を読むのって実に面白いなと思ったから。
また、感化されやすいミーハーな私は社長に続いて、自分の読書感想文を全社員宛に後日共有しようと思い立っているので、それの準備運動的な感じでこのブログにも書くことにした。といっても本の内容に深く踏み込むことはせず、かるーい走り書きだけですが。。。

というわけで、

これからの「正義」の話をしよう

人の命に対して金額的価値を定めた上で、例え人命が失われてたとしても、最終的な利益を追求することはなぜ悪なのか?
道徳的観点、資本主義観点、自由至上主義、様々な観点から世界で起きた実在の事件や、思考実験を通じて、我々人間はどう振る舞うべきなのか?を読者に問いかける本。

喫煙による肺がんの死亡リスクと人間の命の値段、
自ら食べられることを望んだ男と、その男を食べた男について、
妊娠のアウトソーシング
などなど、衝撃的なテーマも並んでいくが、著者(訳者)の文章が抜群にうまく、
どのテーマについても自身で色々と考えながら読み進めていける。

なんか読みやすくて面白い哲学書な〜い?と聞かれたら、私はこの本を勧めたい。

なお、この本の中ではイマヌエル・カントが出て来る。
私はいつかカントの"純粋理性批判"を読もうと心に決めていたのだが、 なかなかきっかけを掴めずにいた。
この本の中で、カントのことが実に魅力的に紹介されており、 また、私自身カントの思想に興味を持ったので、このあとKindleで買って読むつもり。
(ちなみに本書ではカントをはじめ、アリストテレス、ロック、ベンサムなどの過去の哲学者から、ロールズノージックといった現代の哲学者まで、彼らの主張や考えがわかりやすく説明されたりもしているので、哲学書の入門にもよいのでは?と素人の私は思った次第)

下記の新訳版は巻数は多いものの、それは出版社の金儲けのためではなく、
解説にも力を入れており、解説込みでとても良い内容になっているらしい。

というわけでカント読んでみます。

純粋理性批判

iPhoneでGitHub見るのに、CodeHubがかなり良さそう

特に目新しい記事でもなんでもなく今更感もあるかもしれませんが、個人的に良いなと思ったので書いていきます。
こういう前フリ挟むのは今日だけにするつもり(;^ω^)↑

なんとなくGithubiPhoneアプリとかってどんなのがあるのかなーと思って調べてみたら、
こんなの見つけたのでインストールしてみた。

https://itunes.apple.com/us/app/codehub-github-for-ios/id707173885?ls=1&mt=8&at=10l8JW&ct=hatenablog

CodeHub - iOS Client for GitHub

まだちょっとしか使ってないけど、最初の印象としては良い感じ。
やはりWebブラウザから見るのとは操作性が格段に違い、Twitter見るかのような感覚でGitHubブラウジング出来る。
iPhoneGitHub見るなら、入れておいて損はないアプリだと思った。

しかもコード自体はGitHub上に公開されているので、
改善点や、新機能の要望とかあればプルリクも歓迎とのことなので、送って見ると良いかも。

github.com

なお、CodeHubはXamarinで作られている。 仕事で少しだけC#使っていた時期があるけど、もっぱら渋い業務系アプリだったので、Xamarin使ってiPhoneアプリとか作ってみたい。
今は無料で使えるんだよね。
このポストを書くにあたって、Xamarinのことをおさらいしたけど、
Xamarin に関する解説は、下記が参考になりました。

codezine.jp

花粉症ではない、それに近しい何か

rebuildの最近のエピソードでも話されていたようにMoritaさんのブログが一気に公開されたようだ。

rebuild.fm

Publishing 2016 - steps to phantasien

Moritaさんの文章はグイグイ読ませる系の文章で、
個人的にはなんとなく文学の香りがする。
いや、かなり内容としてはTech色の強い内容なのだけど、
文章の雰囲気に文学めいた香りや深い内省の残り香のようなものを感じるのだ。
勿論あくまでこれは個人的な感想である。

ちょっとした休憩時にサラッと読むよりは、ちゃんと時間を取って読みたい文章といった感じ。
というわけで、後日きっちり背筋を伸ばして(そんな心境で)で読むつもりである。楽しみ。

Moritaさんといえば、下記のrebuildエピソードで話していた、
"お前のエントリはニューラル・ネットワークで生成できる!"(※)というセリフが印象的である。
このセリフは現代のテクノロジーをうまく表現した言葉だと個人的に感じた。
というか、私の2016年の名言大賞である。(<=もっとマシな言い回しはなかったのだろうかと我ながら思う)
ちなみにこのセリフが、このエピソードのタイトルにもなっている。
"Your Blog Can Be Generated By Neural Networks"

※正確には言い回しが若干異なっているが、個々の部分がうまく聞き取れなかった。←エントリの部分は退職エントリと言っているのかもしれない。
内容としては差異ないと思うので、こちらで掲載した。
正確なセリフはこうですよ、というのがあればコメントいただけると幸いですm(__)m

ちなみに、この言葉は現代のあらゆるシーンで使えそうである。
お前の音楽なんてニューラル・ネットワークで生成できる!
お前のアート作品なんてニューラル・ネットワークで生成できる!
といった感じだ。

とにかく私はこの言葉を聞いて、いたく共感したし、
何かを発信する人間として、色々と考えされたりもした。
いや、本当に名言だと思いました。

rebuild.fm

話は変わり、最近涙と鼻水が出てきて、常時風邪っぽい。
花粉症ではないと思うが、それに近しい何かではあるな、と毎年考えているなと思ったら、ちょうどPodcastをスタートさせたのも、これぐらいの時期だったことに気づく。
(実際は確認したら4月ぐらいだったので、少し時期はズレているよう)

BrewingHeads - Episode 0

Podcastに出てくれた皆さん、聴いてくれた皆さん、ありがとうございます。
そしてこれからもゆるーく行なって参ります。

それにしても鼻水が止まらない。

過去5年間のサウナの検索数推移

本日は帰宅後、近所の温泉施設に行ってきました。
(私の住んでいる場所は別に温泉地じゃないのに、近所で温泉が湧いているのである♨)

最近サウナにハマっている私は、そこの施設内にある"よもぎサウナ"を楽しんできました。
ちなみにたまたまサウナに入っている時に、よもぎを温泉のスタッフの方が交換していったのだが、どうやら布みたいな入れ物によもぎの葉を詰めたものを、サウナ専用の機器の中に入れて蒸しているようだった。
文章だけだとわかりにくいですね。。。

よもぎの香りはするものの、実際のところ"よもぎ"はどんな感じで使われているのか?と気になっていた私は謎が一つ解けた気分。
サウナ後の幸福感とあいまって、とても満足感がある。

さて、タイトルの通り、過去5年間のサウナの検索数の推移が何となく気になって、調べてみた。
といっても、Google Trendsで検索してみただけ。
ブログに検索結果を貼り付ける方法がパパっとわからなかったので、Twitterを経由して貼り付けている。

ここ5年での検索数の推移に大きな変動は一見ないように見える。
勿論これで何かを判断できるわけではないが。

少区域別のインタレストを参照すると、1位が島根県となっていた。
絶対的な検索ボリュームを表しているわけではないので、実際のところ島根県がサウナ大好きな人々の件というわけにはならないが、なんとなくなにかあるのかとか思ってしまう。。ま、今日はもうそろそろ寝ることにするので、そのうち機会があったら調べてみたい。

では。