kekeのアトリエ

エンジニア、読書@1915keke

Start your Bitcoin app with just 4 lines of code. go-btcrpc

Hey Guys. A great announcement to everyone who wants to use blockchain.

I made a simple bitcoin library to interact with your bitcoin node called go-btcrpc.

What's JSONRPC?

JSONRPC is like JSON but the object is a bit different. Unlike JSON, JSONRPC has a mandatory key like jsonrpc or method.

And all methods of the Http requests are POST. It is used for remote method executing.

In the blockchain application, the infrastructure looks like this. The app server interacts to the blockchain though the bitcoin node server. You can get the docker file of the bitcoin node amacneil/bitcoin.

https://qiita-image-store.s3.amazonaws.com/0/153320/744a8c95-5e4e-73f4-9d4a-5e6d5e0cfcc5.png

How can I create a app?

For my example, I use a golang webframework for my application server. And I use this go-btcrpc package for the interaction between these two.

image.png

How to use this package?

This is extremely easy.

package main

import (
  "fmt"
  btcrpc "github.com/KeisukeYamashita/go-btcrpc"
  )

func main() {
    basicAuth := &BasicAuth{
        Username: os.Getenv("USERNAME"),
        Password: os.Getenv("PASSWORD"),
    }
    c := NewRPCClient(os.Getenv("BTCD_ENDPOINT"), basicAuth)
    address := "my88QLpf2RYYDdNMmDwYvfx6TFc6NXaELa"
    balance := c.GetBalance(address)
    fmt.Print(balance) // 0.13514 BTC
}

That's all. It alreadly got the balance(the mount of bitcoin) of the address. There is more useful methods, see the README.md for more specific infomations about the methods. Not all methods of bitcoin api is implemented!

How to get the bitcoin node?

See the official repo for installation. bitcoin/bitcoin

Remember to set the basic auth option when you start your bitcoind.

How to contribute?

Just send a pull request to this repo. I believe that this project is very cool lets people develop blockchain application much easlier.

Make world a better place. That's why this project is OSS.

Questions

Please feel free to ask questions about this package via email, github, facebook and such like. I've created app on Ethereum,Bitcoin, Ripple, Lisk...etc.

Any questions are welcomed.

Email: 19yamashita15@gmail.com

Github: @KeisukeYamashita

Ripple開発者コミュニティを作りました

f:id:bobchan1915:20180214213318p:plain

TL;DR

  • Rippleの開発者コミュニティを作りました
  • XRP testnetで10000XRP持っていないと入れません
  • Rippleについて熱く議論できればなと思います

->リップル開発者コミュニティ

はじめに

本来ならばslackinのように自動招待をしたかったのですが、忙しくできませんでした。

本質的なRippleを持っているかどうかでチェックしています。 ブロックチェーン関連のSlackコミュニティは以上にサイバーテロ攻撃をされます。(僕もフィッシングやアドレス詐欺など)

なので一回目を通そうと思いました。 自分も何回か攻撃されているし、特定通貨ではコミュニティが荒らされているケースもあります。

背景

RippleはRipple.incによって開発が行われている分散型台帳技術を利用した即時グロス決済システム、 外国為替・送金ネットワークのことで、Bitcoin系やEthereumとは少し違った運用方法をしています。

Rippleはそもそも分散的な社会ということを目標としているわけではなくて「非中央集権」的な社会を目指しています。 ブリッジ通貨、ゲートウェイ、ペイメントなど難しい単語が並んでいて、開発をしていてもハマって、開発が遅くなってしまいます。

日本でも投資対象として、単なる送金通貨対象としてではなくて、エンジニアリング精神を主軸に開発を促進していけるコミュニティがほしいと思って作りました。

テストネットでXRP所有者限定に

なぜテストネットXRP所有者だけがSlackコミュニティに入れるのかというと、それはブロックチェーンハッキングのジレンマが重要な鍵となっています。

暗号通貨を始めとするブロックチェーンおよびそれら自体はセキュアなシステムです。 つまり、ハッカーがブロックチェーン自体をハッキング(51%アタッキングなど)をしても、それ自体の通貨の信用を下げることになり、結果的に誰も使わないので価値が下がってしまいハッキングをした意味が低下します。 また、コインチェックのNEM流出事件では、ほぼ100%の通貨が大幅に下落して(今はまた上がってますが)暗号通貨そのものの価値は下がっていました。

なるべくエンジニアが集まるには、やはりXRP所有者であることがエンジニアとしての責務を果たす動機になるのだと思い、それを参加条件にしました。

テストネットでXRPは容易に手にいれることができます。 なので、XRPを勉強するついでに入手するといいと思います。

仕組み

Metamaskを代表とするブラウザの拡張機能がすでにあればいいのですが、なかったので直接RippleのLedger Testnetに問い合わせています。 その結果を元に招待を送るか否かを判定しています。

かなり簡単なので、放課後ハッカソンみたいなモチベでやりました。

余談にもありますが、あくまでもPHPを使って何かがしたかっただけです。笑

最後に

Rippleは個人的にはEthereumにも、Bitcoinにも似てもいない暗号通貨だと思います。 (技術起因による)命名法から、組織体制、コンセンサスアルゴリズムなど、、、

個人的なことですが、ちょうど最近Bitcoin, Litecoinのサービス開発の受託開発やEthereum上でトークン発行、スマートコントラクトによるアプリ(iOS, Rails)などをし終わって、Ripple開発に着手しました。

もっと日本で文献や知見が増えるためにも、このコミュニティがその背中を押せるようなものになれればいいと願っています。

余談

本サイトはセキュリティ運用やハッキング手法の勉強のためにPHPで作りました。 PHPは人気はないものの、20年間の歴史があって、いたるところに使われています。

Web サイトのサーバー・サイド 82.3%* で PHP が使用。 また PHP ベースの WordPress は、全サイトの 27.2% で利用中。(引用:今学ぶべきプログラミング言語ランキング【2018最新版】)

今まではC/C++, Golang, Ruby, Python, HTML, CSSがメインでしたが、PHPも活用していきたいです。

->リップル開発者コミュニティ

綿矢りさ「蹴りたい背中」が読みたくなる痺れるフレーズ集

自分が2017年読んだ小説の中でトップ3に入るくらい面白かったので紹介します。 「いまさら?」って思うかもしれませんが、大人になってから読むと何倍も楽しめたので、ぜひ皆さんにも読んでみてほしいです。

蹴りたい背中 (河出文庫)

蹴りたい背中 (河出文庫)


綿矢りさとは

Wikepediaによると、綿矢りささんは京都府京都市生まれです。 現在(2017年12月時点)で33歳です。早稲田大学教育学部国語国文科卒業です。

彼女が一躍有名になったのは「インストール」でで第38回文藝賞受賞してからです(当時17歳)。その二年後にこの記事で取り上げる「蹴りたい背中」で芥川賞を受賞して、ミニオンセラーになるなど反響を及びました。

「蹴りたい背中」とは

周りの人間とどうも自分は違う生き物のように感じる主人公が、同級生のオタクであるにな川との交流を沸き立つ、淡い青春を描いています。 この本は、2003年、当時著者が在学中には単行本が刊行されて、第130回芥川龍之介賞を受賞しました。

あらすじ

理科の授業で仲間外れにされたハツは、同じ班のにな川が読んでいる女性ファッション誌のモデル(オリチャン)に目がとまる。ハツは中学生のとき、隣町の無印良品でオリチャンに会ったことがあり、そのことを言うとにな川は興味を持つ。放課後彼の家に呼ばれ、そこでにな川がオリチャンの大ファンであると知る。後日ハツはにな川に頼まれ、オリチャンと会った無印良品へ向かう。そしてにな川の家で休憩する二人だったが、ハツはオリチャンのアイコラ(にな川作)を見つける。ハツは異様な気分になり、にな川を後ろから思い切り蹴り倒す。

その後、にな川が学校を4日間休む。不登校ではないかと言われるも、ハツはにな川の家にお見舞いに行く。実はにな川は徹夜でオリチャンのライブのチケットを取ったため、風邪を引いたのだった。にな川はチケットを4枚買っており、ハツは誰か呼んで一緒に行こうと誘われる。友人は絹代しかいないので、仕方なく絹代を誘って3人でライブに行く。絹代がハツに「にな川はいい彼氏なんじゃないか」「ハツはにな川のことが本当に好きなんだね」と言うが、ハツは「自分の気持ちはそうじゃない」と思っていた。

ライブから帰ると、バスはもう出ていなかった。仕方なくハツと絹代はにな川の家に泊まる。ハツはよく眠れず、ベランダでにな川と話をする。にな川が「オリチャンを一番遠くに感じた」と言ってハツの方を背にして寝転がると、ハツはにな川の背中を蹴ろうとする。指が当たったところでにな川が気づくが、ハツは知らないふりをする。(wikipedia)


読みなるフレーズ

私が勝手ながら、印象だった描写を列挙します。

  • 書き出し

      さびしさは鳴る。耳が痛くなるほど高く澄んだ鈴の音で鳴り響いて、胸を締めつけるから、せめて周りには聞こえないように、 私はプリントを指で千切る。 細長く、細長く。紙を裂く耳障りな音は、孤独の音を消してくれる。気怠げに見せてくれたりもするしね。葉緑体?オオカナダモ?ハッ。っていうこのスタンス。

  • 理科の実験で、自分の椅子が余り物の華奢ないすだった時

    余り者には余り物がしっくりくるのだ。いじめじゃない、ごく自然なことなんだ。

  • はじめてにな川に抱く違和感

    味噌汁の、砂が抜けきっていないアサリを噛みしめて、じゃりっときた時と同じ、ものすごい違和感が一瞬に通り過ぎていく。

  • 友人の友達グループのトランプに誘われて

    どうしてそんなに薄まりたがるんだろう。同じ溶液に使ってぐったり安心して、他人と飽和することは、そんなに心地いいもんなんだろうか。私は余り者も嫌だけど、グループはもっと嫌だ。

  • 陸上の部活動をしているときに思った顧問に対して

    人間に囲まれて先生が舞い上がる度に、生き生きとする度に、私は自分の生き方に対して自信を失っていく。

  • モデルであるオリチャンの雑誌を貪るように読むにな川に対して

    あんなに健康的なものを、よくこれだけ卑猥な目で見られますね。

  • ふと学校で誰にも気にすらされていないことを想って

    学校にいる間は、頭の中ですっと一人でしゃべっているから、外の世界が遠いんだ。

  • 最後ににな川の背中を蹴るハツ

    川の浅瀬に重い石を落とすと、川底の砂が立ち上がって水を濁すように、"あの気持ち"がそこから立ち上がってきて心を濁す。

まとめ

この本の描写は本当に面白くて、読みやすいです。

高校生のときに抱いてた無力感や淡い青春をストレートに描いています。 この冬休みに読んでみてはいかがでしょうか。

最後に好きなフレーズを。

話のネタのために毎日を生きているみたいだった。とにかく"しーん"が怖くて、ボートに浸水してくる冷たい沈黙の水を、つまらない日常の報告で埋めるのに死に物狂いだった。


おすすめの綿矢りさの他の作品

インストール(2001年)

インストール (河出文庫)

インストール (河出文庫)

夢を与える(2006年)

夢を与える (河出文庫)

夢を与える (河出文庫)

かわいそうだね(2012年)

かわいそうだね? (文春文庫)

かわいそうだね? (文春文庫)

JSON-RPC1.0と2.0

この記事はBlockChain Advent Calendar 2017 15日目の記事となっています。

非情報系の3年生なので、技術的に説明不足なことがあったらすみません。

招待制のウォレットを作ったので紹介させていただきます。 これは実験的に招待制にしたので、意味があるかないかはわかりません。

こちらがバランス確認ページです。

f:id:bobchan1915:20171215122514p:plain

そしてこちらが招待された場合のメールです。

f:id:bobchan1915:20171215122724p:plain

JSON-RPCはブロックチェーン開発では非常に重要な反面、JSONで普段開発しなれているので、新たに開発しなくてはいけないのではないかと怖がっている人を多く見かけます。

今回は記事が冗長にならない範囲で、JSON-RPCパッケージを作ったときの「JSON-RPC」に思ったことをお話しします。

開発内容

今回、私が開発したのは以下の通りです。

  • JSON-RPC1.0に対応したライブラリ
  • Bitcoin nodeと通信するためのライブラリ

これらを開発するにあたってJSON-RPCを学ぶことができたのでまとめます。

 JSONRPC2.0とは

JSONRPCの公式リファレンスはプロトコルの一種でRPC(Remote procedure call)の略です。 簡単にいうと遠隔からメソッドをサーバーで実行するものです。

例として以下のようなJSONRPCリクエストがあります。

{"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3}

具体的な仕様としては

  • jsonrpc: バージョンを指定(最新は1.0)
  • method: 実行したいメソッド
  • params:パラメータ
  • id:識別子

があるだけです。識別子があるのはどのリクエストに対してのレスポンス化を明確にするためです。

これを叩くと

{"jsonrpc": "2.0", "result": 19, "id": 3}

のようなresponseが返ってきます。

これは以下のような仕様になっています。

  • jsonrpc: バージョンを指定(bitcoindの最新は1.0)
  • result: レスポンスのjson
  • id:識別子

なぜJSONRPC2.0を使うのか

JSONRPCを使うのには以下の理由があります。 - RESTfulなサーバーを立てる必要がない - エンドポイントが非常に透明性が保たれる - メソッドを実行できるため「したいことをできるようになる」 などと行ったようなメリットがあります。

RESTfulなサーバーを立てる必要がない

私自身、Railsや他のRESTfulを意識したフレームワークを使ったことはありますが、

GET /new
POST /create
GET /show
...

などと少し面倒です。どれがGET、POSTで、どんなエンドポイントに何をroutingしなくてはいけないのか、、、 考えるだけで面倒です。ならば、JSON-RPCを使うと非常に便利になるのです。 以下のようなメソッドを実装するだけで以下のように簡単に実装ができます。

Endpoint              /user
methods              getName
                             createUser
                             updatePassword
...
{"jsonrpc": "2.0", "method": "getName", "params": {"user_id": 23}, "id": 3}

また、3回のリクエストが同時に来たとしましょう。

# Ayumiが
{"jsonrpc": "2.0", "method": "getName", "params": {"user_id": 23}, "id": 1}
# Kekeが
{"jsonrpc": "2.0", "method": "getName", "params": {"user_id": 23}, "id": 2}
# Tunakichiが
{"jsonrpc": "2.0", "method": "getName", "params": {"user_id": 23}, "id": 3}

レスポンスが以下の順で返ってきました。しかしながら、識別子があるので、どのリクエストに対応するレスポンスかすぐ知ることができます。

{"jsonrpc": "2.0", "result": "Tunakichi",  "id": 3}
{"jsonrpc": "2.0", "result": "Ayumi", "id": 1}
{"jsonrpc": "2.0", "result": "Keke",  "id": 2}

非常に便利なんです。 JSON-RPCの歴史までも知りたい人はwikipediaをご参照ください。

JSON-RPC1.0とは

JSON-RPC1.0はJSON-RPC2.0と比較して

  • jsonrpc:2.0といったようなバージョン指定がない
  • paramsは必須。2.0では無視可能.

などの特徴があります。 この規格にBitcoinは対応しているのです。

まとめ

JSON-RPCとは - POSTメソッドでmethodをJSONの中に組み込んで実行するもの - リモートでメソッドを実行できるので、RESTに従う必要がなくなる - JSONをHTTPリクエストで飛ばすところが非常に軽い実装になる

以上の理由より推奨されます。

誰もがつまづくDjangoをHerokuにデプロイするときあるある

はじめに

今回は「ツナキチ」というニューラルネットワークによる画像認識を用いて人間の顔が一体何% ネコっぽいのかを調べられるWebサイトを作りました。 時間がなくてsafariではデザインが崩れてしまってます。いずれ直します!) 明示的にHerokuでデプロイしていることを示したいので、リンクを貼らさせてもらいます。 https://tunakichi.herokuapp.com/

使ったバージョンは

  • python : v3.6.1
  • django : v1.11.7

です。

コメント、いいね大歓迎です。誤字や間違いは指摘していただけると助かります。

前提

ここではすでにアプリは完成していて、いまからデプロイするぞ!という方を前提にしています。 そしてデプロイをする上で、困ったことを紹介していきます。

デプロイ

pushする

まず、herokuのアプリケーションをherokuに登録しなければなりません。

heroku create <アプリ名>

で登録をすることができます。 一応、うまく繋がっているか確認してください。(これはgitとも接続されているか確認できるので初心者の人は使ってください。)

git remote -v

そして以下のように出ればおっけいです。

hogehoge:~/tunakichi[master]>> git remote -v
heroku  https://git.heroku.com/<リポジトリ名>.git (fetch)
heroku  https://git.heroku.com/<リポジトリ名>.git (push)
origin  git@github.com:KeisukeYamashita/<リポジトリ名>.git (fetch)
origin  git@github.com:KeisukeYamashita/<リポジトリ名>.git (push)

そしたら次にClearDBを作っていきます。簡単にいうとheroku上のDBです。

heroku addons:add cleardb:ignite

そしてherokuの環境変数を変更してください。

heroku config:set DISABLE_COLLECTSTATIC=1

ここでアプリ側の設定を変えなければなりません。wgsi.pyに以下に変更してください。

import os

from dj_static import Cling
from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "<アプリ名>.settings")

application = Cling(get_wsgi_application())

これで設定ができたので、pushすればデプロイできます。

git add .
git commit -m "hogehoge"
git push origin master
git push heroku master

確認

git push heroku masterのときにURLがすでに表示されるのですが、開くには以下のコマンドが便利です。

heroku open

またマイグレーションが必要な方は以下のコマンドでしてください。

heroku run python manege.py migrate

さらにシードデータがあるときはloaddataで入れることができます。 DjangoではJSONによるシードデータを入れる方法と、.YAMLによるシードデータの入れ方があります。

heroku run python manegy.py loaddata <シードデータファイル(JSONorYAML)>

これで再起動すると正しく動くはずです。

heroku restart

トラブルシューティング

[ケース1] herokuへのpushがrejectされるNo default Language

これはDjangoをデプロイしようとしている人の多くが直面する問題です。

原因

ディフォルトでなんの言語を使うのかを指定していないことが問題です。 herokuでは次のような言語がサポートされていて、簡単にいうとどの言語を使いますかという設定をしないといけない。

image.png

今回はPythonを使います。

解決策

まず、pythonのバージョンをruntime.txtに書き込みます。

echo $(python -V) > runtime.txt

次にローカルで使っているパッケージをherokuでも使いたいのでrequirement.txtに記載します。

pip freeze > requirement.txt

そしてpushすればリジェクトされないはずです。

[ケース2]無料枠の落とし穴。「たまに見ると落ちているんですけど、、、」

原因

herokuの無料枠では、30分間アクセスがなれけばスリープモードに入ってしまい、再起動が必要となります。

解決策

毎20分ごとぐらいにherokuに自分でアクセスしてもいいのですが、これを自動化しましょう。 デプロイしたWebサーバー自体が自分にアクセスするように組めばいいのです。

'<アプリ名>/wgsi.pyに以下を追加してください。

import os
import threading
import requests
import time


def awake():
    while True:
        try:
            print("Start Awaking")
            requests.get("http://hogefuga.herokuapp.com/")
            print("End")
        except:
            print("error")
        time.sleep(300)

t = threading.Thread(target=awake)
t.start()

これによって自分にアクセスするようになるので、サーバーがスリープになるようなことはなくなります。

[ケース3]変な名前のモジュールがない。No module name _tkinker

原因

これはグラフを書くのによく使われるmatplotlibライブラリに含まれるmoduleであり、これがherokuでインストールされていないことが原因です。

解決策

まずサーバーにmatplotlibを使うことはおかしいです。笑 (僕はローカルで使っていた数値計算アルゴリズムをそのまま.pyファイルとしてサーバーの計算部分に使おうとしたら勝手に混入していました)

なのでアプリ内の全import <Matploblib>を削除すれば解決できます。

[ケース4]ケース3の一般系。No module name hogehoge

原因

とあるhogehogeモジュールがないといっています。 これは抽象度が高いので、原因がいくつか考えられると思います。

原因1. そんなモジュールなんてない説

本当にそのモジュールがあるかを確認してください。 タイピングミスでmodule名が変わっていないですが?

原因2. herokuにインストールされていない説 ← 定説

ローカルで追加で開発をして、再度pushしたとします。 ローカル環境で新しくpipで導入したことをherokuは知りません。

なのでrequirement.pyを更新することが必要です。

pip freeze > requirement.txt

ScalaでMVCを構築する手順

はじめに

Scalaの超軽量フレームワーク「Scalatra」上でMVC構造を構築するまでを解説します。 Scalatraだけでなく、Scalaは記事が少なすぎる

タグを見るとわかるが、Scalatraに限って8です。

image.png

圧倒的少なさから入門もしにくいし、、、 そのせいで、普段はSwift,Go, Python, Rubyを書きますが、Scalaをやると非常に面白いが文献が少なかった。 なので今回はWebアプリケーション開発できるようにMVCを構築することから始めます。

ニーズ

自分は普段からRailsや、Django、Ginなどの「フルスタックフレームワーク」で開発をしたりしますが、MVC構造といってもあまりにも自由がないです。 Railsなんてブラックボックス化がひどいし、体験的なプログラミングをしてない気がします。

前提

本記事は以下のことを対象にしています。

  • すでにbstなどでビルトが済んでいる方

を対象にしています。

ディレクトリ構造

若干の違いはあるかもしれませんが、以下のようなディレクトリ構造をしているはずです。

src
├── main
│   ├── java
│   ├── resources
│   │   └── logback.xml
│   ├── scala
│   │   ├── ScalatraBootstrap.scala
│   │   └── com
│   │       └── example
│   │           └── app
│   │               └── ScalaServlet.scala
│   └── webapp
│       └── WEB-INF
│           └── web.xml
└── test
    ├── java
    ├── scala
    │   └── com
    │       └── example
    │           └── app
    │               └── MyScalatraServletTests.scala
    └── scala-2.12

初心者な私には、どこに何を配置したらいいのかわからなかったのですが、Goのpackageを思いました。 Packageとは、名前空間と覚えればオッケイ。

つまり、MVCにするならばpackageで区切ればよくて、次の問題はどのように明示的にMVCであることを透明性を保ったまま構築できるかです。

もちろんapp以下に構築するのがいいかもしれません。 先程のディレクトリツリーを抜粋して以下のように変更します。

src
├── main
│   ├── java
│   ├── resources
│   │   └── logback.xml
│   ├── scala
│   │   ├── ScalatraBootstrap.scala
│   │   └── com
│   │       └── example
│   │           └── app
│   │               └── ScalaServlet.scala
│   │               └── view
│   │               └── controller
│   │               └── model
│   └── webapp
│       └── WEB-INF
│           └── web.xml
└── test

ScalaServlet.scalaを中心に、MVCを構築すると透明性の高く、開発しやすいのでおすすめです。

golang+gormで画像をバイナリ化してDBに保存する

はじめに

過去に長期インターンでRubyonRailsをやっていたのでその経験も活かして記事にしました。 はてなブログ記事

やりたいこと

今回は自分のプロフィール(冬仕様)であるこの画像をバイナリ化してDBに保存しようと思います。(184MB)

この記事はRuby on Rails(RoR)を例にしてあげますが、全く理解できなくてもスルーしても読めるようにしたいとは思っています。

今回使用したVersionは以下の通りです。

  • Golang 1.9.1
  • gorm 0.6.3
  • Ruby on Rails 5.0.3(参考用)

より良い記事にしたいので 誤字や間違いがありましたらコメントなどをいただけると幸いです。

方針

Ruby on Railsが、やすやすとここらへんを実装していて、自分も実装経験があるので、参考にしながら進めていきますが Railsはあくまでも比較のためにあるだけなので、さらっと読みたい人はGolang部分だけを見てください。

文法的なことはあまり解説しませんがコンタクトしてくださればわかる範囲で答えます!

実装

DB

この記事では明示的にファイルを示したいのでオブジェクトをFileと名前をつけます。 命名法に違和感を抱く人がいたらすみません。

Railsでは以下のように書いて、ActiveRecord(Ruby製のORM)に働いてもらいます。

class File < ActiveRecord::Migration
  def change
    create_table :files do |t|
      t.binary :upload_file
      t.timestamps
    end
  end
end

なんですがupload_fileがRubyでいうbinaryでDBに書き込まれます。 正直、ORMは仲介人くらいなんでどうでもいいです。要はDBにどんなデータ型で保存されるかが重要なんです。

実際に見てみると(create_atなど省略しています)

Field Type Null Key Default Extra
id int(11) NO PRI NULL auto_increment
upload_file mediumblob YES NULL

となってました。 つまりupload_fileの方はmediumblob(8bit整数)。 これ[]byteと同じ???!!! だってgoでは[]byteと指定するとVARBINARYというデータ型になり、MySQLでは等価だからです。(※厳密には違う) []byteとは[]uint8のaliasです。詳しくは以下リンクで。 https://stackoverflow.com/questions/22950392/difference-between-uint8-byte-golang-slices

では、どのようにしたらbinary fileにできるのでしょうか? ちょっとその前に、これを実装するために、データベースを作るためのdb.goを用意しておきます。

package main

import (
  "os"
  "log"
  "fmt"
  "encoding/binary"
  "image/jpeg"
  "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
  "bytes"
)

var db *gorm.DB

type File struct {
    gorm.Model
    Source             []byte  
}

func init(){
  db, err = gorm.Open("mysql", "host:hogehoge@mysql/database")
    if err != nil {
        panic(err)
    }
  f = &File{}
  db.CreateTable(f)
  defer db.Close()

これを以下のコマンドで実行し、データベースを作成します。

$ go run db.go

データベース内容

Field Type Null Key Default Extra
id int(11) NO PRI NULL auto_increment
upload_file VARBINARY YES NULL

ImageのBinary化

 最初に

JPEGを画像を扱うためimage/jpegがありますが、この他に標準ではGIF,PNGに対応しています。 この他の拡張子を使う場合は、他にパッケージを使うしかないようです。

Railsでは<form>によってアップロードされたファイルはActionDispatch::Http::UploadedFile クラスなのでReadメソッドで読んでDBに保存すればよかったのですがGolangではどのような感じでしょう。

ディレクトリ構造は以下のようになっています。

.
├── db.go           ← DB作成用
├── image.JPG  ←プロフ画
└── main.go    ← 実行フィアル

以下にコードを示します。 また、最終的なコードはこの記事の最後に書いています。

    file, err := os.Open("image.JPG")
  if err != nil {
        log.Fatal(err)
    }

  img, err := jpeg.Decode(file)
  if err != nil {
      log.Fatal(err)
  }
  file.Close()

  buffer := new(bytes.Buffer)
    if err := jpeg.Encode(buffer, img, nil); err != nil {
        log.Println("unable to encode image.")
    }
  imageBytes := buffer.Bytes()

  f := &File{Source:imageBytes}
  db.Create(u)

解説

それではすこし解説をします。

まず開きたい画像ファイルのパスをPATH部分にいれてos.Open(<PATH>)で開きます。 問題なくいくと*os.File型の戻り値が返ってきます。

    file, err := os.Open("image.JPG")
  if err != nil {
        log.Fatal(err)
  }

次にimage/jpegDecodeメソッドを使ってimage.image型の変数を作ります。 なぜこのimage.image型を作るかというと、あとにエンコーディングをするときに必要だからです。

また、ここで注意したいのはかならずfile.Close()をやってください。

  img, err := jpeg.Decode(file)
  if err != nil {
      log.Fatal(err)
  }
  file.Close()

そしてファイルを生成してください。 同じくout.Close()をすることを忘れないでください。

そしてbytesでバッファを扱うための便利なパッケージがあります。bytes.Bufferは書き込み用のインターフェイスを持っています。 newbyte.Bufferを初期化して、imgがもつimage.image型の変数をともにjpeg.Encodeに渡しています。

  buffer := new(bytes.Buffer)
    if err := jpeg.Encode(buffer, img, nil); err != nil {
        log.Println("unable to encode image.")
    }
  imageBytes := buffer.Bytes()

json.Encodeをなぜこのようにするかというとリファレンスを読んでみたらすぐわかります。

func Encode(w io.Writer, m image.Image, o *Options) error

byte.Bufferは書き込み用のインターフェイスio.Writerを持っているのでわたすことができます。 そして、もとの[]byte型はBytes()で取得できるのでimageBytesに変換することができます。

imageBytes := buffer.Bytes()

そしてこれを構造体に渡してDBに保存して終了です!

f.Source = imageBytes
db.Create(f)
db.Close()

これでこの記事終了です! お疲れ様でした!!

終わりじゃない、、、

というもの、まだ問題が残っています。 それはこのままでは動かないのです。

ERROR 1406 (22001): Data too long for column 'source' at row 1

実は今回のbinaryは長すぎるのです。 というもの、冒頭で、「VARBINARYMIDDLEBLOBは等価だ!Rails通りだぜ!」みたいなことを書きました。

しかし、ここにきて痛い目に合うのです。 データの大きさが鍵となっています。

余談(Blob型の歴史)

もともとMySQLではバイナリ型はblob型で一括してまとめてました。(たしか5.0.7ぐらいまで) しかしバージョンが上がると、VARBINARYVARCHARが出てきました。 MySQL公式ドキュメントから参照しています。

image.png

簡単な乗数の計算ですがだいたいblob型の大きさは

  • TINYBLOB:255B
  • BLOB:65,535B
  • MIDDLEBLOB:16MB
  • LONGBLOB:4GB

であり、VARCHARVARBINARYはというと255Bです。 到底足りません。

解決する

話をもとに戻します。

データのSizeに原因があることがわかりました。 冒頭のこの部分に注目してください。

スクリーンショット 2017-11-19 5.23.45.png

到底足りません!LONGBLOB型が必要です

Railsのときは指摘してなかったじゃないか!と思われるかもしれませんが、その通りで、勝手にデフォルトでなっていました。 Railsに親しい人は当然だと思いますが、実は:limit => 3.gigabyteとして指定してすることで、今回の場合でいうとLongBlobに変更することができるのです。

class File < ActiveRecord::Migration
  def change
    create_table :files do |t|
      t.binary :upload_file, :limit => 3.gigabyte
      t.timestamps
    end
  end
end

同様にgormにだってできます。 gormは構造体をマッピングするという特徴があるので、構造体をいじればどうにかなりそうですよね。 実際もそうで指定したいメンバにgorm:"size:70000などとしてあげるとLONGBlOBに変わります。

type File struct {
    gorm.Model
    Source             []byte    `gorm:"size:70000"`
}

このようにしてSizeを変更できたので最後に実行して終わりです。

$ go run main.go

DBを確認すると人間には優しくない文字列がありますが、それがバイナリです。 最後までお読みしてくださりありがとうございました。 お疲れ様でした。

ソースコード

package main

import (
  "os"
  "log"
  "fmt"
  "image/jpeg"
  "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
  "bytes"
)

type File struct {
    gorm.Model
    Source []byte `gorm:"size:70000"`
}

func main(){
   db, err := gorm.Open("mysql", "host:hogehoge@mysql/database)
    if err != nil {
        panic(err)
    }
    defer db.Close()

  file, err := os.Open("image.JPG")
  if err != nil {
        log.Fatal(err)
    }

  img, err := jpeg.Decode(file)
  if err != nil {
      log.Fatal(err)
  }
  file.Close()

  buffer := new(bytes.Buffer)
    if err := jpeg.Encode(buffer, img, nil); err != nil {
        log.Println("unable to encode image.")
    }
  imageBytes := buffer.Bytes()

  f := &File{Source:imageBytes}
  db.Create(f)
  db.Close()
}

そしてrunすると実行できます。

go run main.go

余談

最近後輩がこの本を読んでいました。 実際、python以外はプログラミングの本を読んだことがないので、そろそろ読まないとなって思います。

スターティングGo言語

スターティングGo言語

ブログをはじめました

どうも。@1915kekeです。 今日からブログを開始します!

自己紹介

最初に簡単に自己紹介すると

  • Twitterアカウントは@1915kekeです。気軽にフォローしてください。
  • 大学3年生
  • 趣味は読書、プログラミングです。
  • 帰国子女でTOEIC945点もってます。
  • ロボットを毎年作っていました。
  • 夜型です。
  • 好きな食べ物は梅しそです。

最近はキーボード配列を変えて遊んでました。 タイピングゲームは寿司打が最高傑作です。

ブログで取り上げる内容

内容は以下の内容を取り上げていきたいと思います。

  • エンジニアリングに関すること(Go, ruby, Python, 電子工作など)
  • 本について(感想、エッセイなど)
  • 暗号通貨について(Bitcoin, Ethereumなど)

記事に対するコメントは歓迎しますので、ぜひ感想でも、コメントでもください。

泣いてよろこびます。

また、読者になっていただけるとさらに嬉しいです。

これからよろしくお願いします。