imgcat
Posted on
在 iTerm2
中我们可以直接查看图片,是不是有点酷炫。
为了使图片能够正确的展示,我们需要使数据满足特定的协议格式。
1
| ESC ] 1337 ; File = [optional arguments] : base-64 encoded file contents ^G
|
按照上面的格式我们将数据分为 header
,body
和 footer
三个部分,而 header
和 footer
相对固定,我们使用 strings.NewReader
封装返回一个 io.Reader
,而中间变化的 base64
编码的数据,我们需要利用 base64.NewEncoder
来对输入的数据进行编码,最后将三个部分利用 io.MultiReader
整合成一个 io.Reader
,然后利用 io.Copy
将数据传输到 w
中,然而 base64.NewEncoder
返回的是一个 io.WriteCloser
,而我们想要的是一个 io.Reader
,这种转化我们可以借助 io.Pipe()
实现,io.Pipe()
会返回一个 PipeReader
和一个 PipeWriter
,当有数据往 PipeWriter
中写时,PipeReader
就可以获得相应的数据,通过这种方式,我们就可以间接地将一个 Writer
转换为一个 Reader
来使用了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| func copy(w io.Writer, r io.Reader) error { header := strings.NewReader("\033 ] 1337 ; File = inline=1 : ") footer := strings.NewReader("\a\n") pr, pw := io.Pipe() go func() { defer pw.Close() wc := base64.NewEncoder(base64.StdEncoding, pw) _, err := io.Copy(wc, r) if err != nil { pw.CloseWithError(err) return } if err := wc.Close(); err != nil { pw.CloseWithError(err) return } }() mr := io.MultiReader(header, pr, footer) _, err := io.Copy(w, mr)
return err }
|
为了在下面的 imgcat
方法中使用 io.Copy
接口,我们需要自己实现一个 io.Writer
,在 NewWriter
函数中我们返回自己 writer
结构体,并且同时起了一个goroutine
来获取 PipeWriter
写的数据,同理,PipeWriter
中写入的数据可以从相应的 PipeReader
获取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package imgcat
type writer struct { pw *io.PipeWriter done chan struct{} }
func (w *writer) Write(p []byte) (int, error) { return w.pw.Write(p) }
func (w *writer) Close() error { if err := w.pw.Close(); err != nil { return err } <-w.done return }
func NewWriter(w io.Writer) io.WriteCloser { pr, pw := io.Pipe() wc := &writer{pw, make(chan struct{})} go func() { defer close(w.done) err := copy(w, pr) pr.CloseWithError(err) }() return wc }
|
在 imgcat
方法中主要是通过 io.Copy
将路径 path
对应的文件拷贝到我们上面的 Writer
中,NewWriter
指定 os.Stdout
作为我们的输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| func imgcat(path string) error { f, err := os.Open(path) if err != nil { return err } defer f.Close()
wc := imgcat.NewWriter(os.Stdout) if _, err = io.Copy(wc, f); err != nil { return err } return wc.Close() }
func main() { if len(os.Args) != 2 { return } err := imgcat(os.Args[1]) if err != nil { fmt.Println(err) } }
|