- 投稿日:2020-08-08T15:16:52+09:00
Goで画像をクリップボードに保存、読み取るライブラリを作った
こんにちわ
ゴリラです画像をクリップボードにコピー、コピーした画像をクリップボードから読み取るGoのライブラリ clipboard-image をつくったので紹介します。
このライブラリはMac、Linux、Windowsで動作します。先日 Goでソースコードを画像化するCLIを作った で紹介したCLIではこのライブラリを使っています。
使い方
関数は2つのみ
CopyToClipboard
ReadFromClipboard
画像ファイルをクリップボードにコピーするときは
CopyToClipboard
クリップボードから画像ファイルを読み取るときは
ReadFromClipboard
仕組み
クリップボードへのアクセスは外部コマンドを用いています。
それらをGoでラップしてライブラリとして提供しています。それぞれ次になっています。
OS コマンド Mac OS osascript Windows PowerShell Linux xclip Mac OSではApple Scriptを使えばクリップボードにアクセスできます。
Apple Scriptは標準でMac OSに入っているのでライブラリ意外は特に不要です。# copy to clipboard osascript -e 'set the clipboard to (read "/path/to/file.png" as TIFF picture)' # write file from clipboard osascript -e 'write (the clipboard as «class PNGf») to (open for access "/tmp/file.png" with write permission)'WindowsではPowershellでクリップボードにアクセスできます。
# copy to clipboard PowerShell -Command Add-Type -AssemblyName System.Windows.Forms;[Windows.Forms.Clipboard]::SetImage([System.Drawing.Image]::FromFile('/path/to/file.png')); # write file from clipboard PowerShell -Command Add-Type -AssemblyName System.Windows.Forms;$clip=[Windows.Forms.Clipboard]::GetImage();if ($clip -ne $null) { $clip.Save('/path/to/file.png') };Linuxでは
xclip
というコマンドをインストールことでクリップボードにアクセスできます。# copy to clipboard xclip -selection clipboard -t image/png < /path/to/file.png # write file from clipboard xclip -selection clipboard -o > /path/to/file.pngこれらのコマンドをGoの
exec.Command
で実行することで、Goでクリップボードにアクセスできます。詰まったポイント
xclip
を使う時、標準入力でファイルの中身を渡すのですが、次のように書いても標準入力に渡っていかないです。標準入力に渡すには
StdinPipe
でpipeを取得してファイルの中身を書き込んだ後に stdin を閉じる必要あります。
xclip
の実装はatotto/clipboardを参考にしました。さいごに
クリップボード周りは結構苦戦して、いろいろな方に質問してなんとか作れました。
ライブラリとしてはまだ完成形とは考えていなくて、今後も拡張していきたいと考えています。ちなみに、この記事の画像はVimで書いたコードをcode2img.vimでクリップボードにコピーしてサクッとQiitaにアップロードしたものです。
当初手軽にVimでコードを画像化したいという目的を達成できて、実際使って便利ので満足しています。コードブロックが使えないチャットやTwitterといったSNSでコード貼りたいときにぜひCLIを使ってみてください。
- 投稿日:2020-08-08T01:28:19+09:00
【GoネットワークプログラムでIPv6対応させる】自分のグローバルアドレスを知る方法
理由
ネットワークプログラムで、自身のグローバルIPアドレスを知る必要がある場合がある。その場合のコード例をメモしておく
参考コード
main.gopackage main import ( "fmt" "github.com/skubota/extip" ) func main() { addr,err := extip.GetIP() if err != nil { fmt.Errorf("Address cannot get : %s\n", err) }else{ fmt.Printf("Adress : %s\n",addr) } }
- 結果(IPv6/IPv4対応クライアント)
Adress : 2xxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
- 結果(IPv4のみ対応クライアント)
Adress : 2xx.xxx.xxx.xxx解説
標準パッケージではないが、「skubota/extip」というIPv6対応のSTUNで通信のあったソースIPアドレスを知ることができます。
アンチパターン
あまり、これがアンチパターンだというものはないかと思いますが、IPv4のみの返答しかできないHTTPサーバやDNSサーバを使ったライブラリやコードがあるので注意が必要です。
まとめ
IPv6対応のパッケージ・コードとなっているか確認しよう
- 投稿日:2020-08-08T01:20:49+09:00
【GoでIPv6】GoネットワークプログラムでIPv6対応させるためのアドレス+ポート組み合わせの作り方
理由
「IPアドレス:ポート番号」の文字列はいろいろな箇所で意味を持った使われ方をするので、よく利用する記法ではあるが、
Goで書かれたネットワークプログラムをIPv6に対応させるためには少し考慮する必要がある。その場合のコード例をメモしておく参考コード
main.gopackage main import ( "fmt" "net" ) func main() { // 対象のアドレスをスライスに入れる addrs := []string{"2001:db8::1", "192.168.0.1", "2001:db8:0:0:1:0:0:1", "::1", "2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa", "2001:db8::aaaa:0:0:1", "::192.0.2.1"} // スライスを1つづつチェック for i, v := range addrs { // 例として:80を付加していく ip := net.ParseIP(v) fmt.Printf("%d: %s = %s\n", i, v, net.JoinHostPort(ip.String(), "80")) } }
- 結果
0: 2001:db8::1 = [2001:db8::1]:80 1: 192.168.0.1 = 192.168.0.1:80 2: 2001:db8:0:0:1:0:0:1 = [2001:db8::1:0:0:1]:80 3: ::1 = [::1]:80 4: 2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa = [2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa]:80 5: 2001:db8::aaaa:0:0:1 = [2001:db8::aaaa:0:0:1]:80 6: ::192.0.2.1 = [::c000:201]:80解説
net.JoinHostPortで結合をするだけ
なぜこのような処理にしなければいけないかは、アンチパターンを見てみましょうアンチパターン
やってしまいがちなアンチパターンはそのまま文字列処理してしまうこと。
main.gopackage main import ( "fmt" ) func main() { // 対象のアドレスをスライスに入れる addrs := []string{"2001:db8::1", "192.168.0.1", "2001:db8:0:0:1:0:0:1", "::1", "2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa", "2001:db8::aaaa:0:0:1", "::192.0.2.1"} // スライスを1つづつチェック for i, v := range addrs { // 例として:80を付加していく fmt.Printf("%d: %s = %s:%d\n", i, v, v,80) } }結果は以下の通りとなります
- https://play.golang.org/p/oHWvhw303lF0: 2001:db8::1 = 2001:db8::1:80 1: 192.168.0.1 = 192.168.0.1:80 2: 2001:db8:0:0:1:0:0:1 = 2001:db8:0:0:1:0:0:1:80 3: ::1 = ::1:80 4: 2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa = 2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa:80 5: 2001:db8::aaaa:0:0:1 = 2001:db8::aaaa:0:0:1:80 6: ::192.0.2.1 = ::192.0.2.1:80IPv6アドレスの場合のアドレスリテラル表記で処理しないことが問題となります
まとめ
IPv6アドレスをそのまま文字列結合するのはNG。
- 投稿日:2020-08-08T01:20:49+09:00
【GoネットワークプログラムでIPv6対応させる】アドレス+ポート組み合わせの作り方
理由
「IPアドレス:ポート番号」の文字列はいろいろな箇所で意味を持った使われ方をするので、よく利用する記法ではあるが、
Goで書かれたネットワークプログラムをIPv6に対応させるためには少し考慮する必要がある。その場合のコード例をメモしておく参考コード
main.gopackage main import ( "fmt" "net" ) func main() { // 対象のアドレスをスライスに入れる addrs := []string{"2001:db8::1", "192.168.0.1", "2001:db8:0:0:1:0:0:1", "::1", "2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa", "2001:db8::aaaa:0:0:1", "::192.0.2.1"} // スライスを1つづつチェック for i, v := range addrs { // 例として:80を付加していく ip := net.ParseIP(v) fmt.Printf("%d: %s = %s\n", i, v, net.JoinHostPort(ip.String(), "80")) } }
- 結果
0: 2001:db8::1 = [2001:db8::1]:80 1: 192.168.0.1 = 192.168.0.1:80 2: 2001:db8:0:0:1:0:0:1 = [2001:db8::1:0:0:1]:80 3: ::1 = [::1]:80 4: 2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa = [2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa]:80 5: 2001:db8::aaaa:0:0:1 = [2001:db8::aaaa:0:0:1]:80 6: ::192.0.2.1 = [::c000:201]:80解説
net.JoinHostPortで結合をするだけ
なぜこのような処理にしなければいけないかは、アンチパターンを見てみましょうアンチパターン
やってしまいがちなアンチパターンはそのまま文字列処理してしまうこと。
main.gopackage main import ( "fmt" ) func main() { // 対象のアドレスをスライスに入れる addrs := []string{"2001:db8::1", "192.168.0.1", "2001:db8:0:0:1:0:0:1", "::1", "2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa", "2001:db8::aaaa:0:0:1", "::192.0.2.1"} // スライスを1つづつチェック for i, v := range addrs { // 例として:80を付加していく fmt.Printf("%d: %s = %s:%d\n", i, v, v,80) } }結果は以下の通りとなります
- https://play.golang.org/p/oHWvhw303lF0: 2001:db8::1 = 2001:db8::1:80 1: 192.168.0.1 = 192.168.0.1:80 2: 2001:db8:0:0:1:0:0:1 = 2001:db8:0:0:1:0:0:1:80 3: ::1 = ::1:80 4: 2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa = 2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa:80 5: 2001:db8::aaaa:0:0:1 = 2001:db8::aaaa:0:0:1:80 6: ::192.0.2.1 = ::192.0.2.1:80IPv6アドレスの場合のアドレスリテラル表記で処理しないことが問題となります
まとめ
IPv6アドレスをそのまま文字列結合するのはNG。