2
1

More than 3 years have passed since last update.

[Go] 画像の拡張子の変更をするCLIコマンドを作る

Posted at

はじめに

このまえ、Gopher道場 の自習室とやらに参加させてもらい、前々から気になっていたGopher道場の課題をgithubにて探して、(やっていいのかな…?)演習しました。

その課題1ってのが画像の拡張子変更 でした。とりあえずアウトプットをしたいので、ここに色々書いていきます。また、使用したパッケージから、flag,os,path/filepath,imageについて、ちょっとだけ解説していきます。

環境

# go version
go version go1.15.2 linux/amd64

# tree
.
|- conversion
|       |- conversion.go
|- go.mod
|- main.go

実際のコード

とりあえず、リポジトリはここです

main.go
package main

import (
    "cvs/conversion"
    "flag"
    "fmt"
    "os"
    "path/filepath"
)

var (
    extension string
    imagepath string
    dirpath   string
)

func main() {

    flag.StringVar(&extension, "e", "jpeg", "拡張子の指定")
    flag.StringVar(&imagepath, "f", "", "変換するファイルのパスの指定")
    flag.StringVar(&dirpath, "d", "", "変換後のファイル名の指定")
    flag.Parse()
    err := conversion.ExtensionCheck(extension)
    if err != nil {
        fmt.Println("Error:", err)
        os.Exit(1)
    }

    err = conversion.FilepathCheck(imagepath)
    if err != nil {
        fmt.Println("Error:", err)
        os.Exit(1)
    }

    err = conversion.DirpathCheck(dirpath)
    if err != nil {
        fmt.Println("Error:", err)
        os.Exit(1)
    }

    f := filepath.Ext(imagepath)
    err = conversion.FileExtCheck(f)
    if err != nil {
        fmt.Println("Error:", err)
        os.Exit(1)
    }

    fmt.Println("変換中・・・")

    err = conversion.FileExtension(extension, imagepath, dirpath)
    if err != nil {
        fmt.Println("Error:", err)
        os.Exit(1)
    }
}
conversion.go
/*
Conversion は 画像の拡張子の変更を行うためのパッケージです。
*/
package conversion

import (
    "errors"
    "fmt"
    "image"
    "image/gif"
    "image/jpeg"
    "image/png"
    "os"

    _ "image/gif"
    _ "image/jpeg"
    _ "image/png"
)

const (
    JPEG = "jpeg"
    JPG  = "jpg"
    GIF  = "gif"
    PNG  = "png"
)

// -e で指定した拡張子が対応しているか、判断します。
func ExtensionCheck(ext string) error {
    switch ext {
    case JPEG, JPG, GIF, PNG:
        return nil
    default:
        return errors.New("指定できない拡張子です" + ext)
    }
}

// -f で指定したファイルが存在するか、判断します。
func FilepathCheck(imagepath string) error {
    switch imagepath {
    case "":
        return errors.New("ファイルの指定がされてません")
    default:
        if f, err := os.Stat(imagepath); os.IsNotExist(err) || f.IsDir() {
            return errors.New("ファイルが存在しません" + imagepath)
        } else {
            return nil
        }
    }
}

func DirpathCheck(dirpath string) error {
    switch dirpath {
    case "":
        return errors.New("変換後のファイル名が指定されていません")
    default:
        return nil
    }
}

func FileExtCheck(imagepath string) error {
    switch imagepath {
    case "." + JPEG, "." + JPG, "." + GIF, "." + PNG:
        return nil
    default:
        return errors.New("指定したファイルが対応していません。:" + imagepath)
    }
}

func FileExtension(extension string, imagepath string, dirpath string) error {
    exFile, err := os.Open(imagepath)
    defer exFile.Close()
    if err != nil {
        return errors.New("os.Create失敗")
    }

    output, err := os.Create(dirpath)
    defer output.Close()
    if err != nil {
        return errors.New("output失敗")
    }

    img, _, Err := image.Decode(exFile)
    if Err != nil {
        return errors.New("Decode失敗")
    }

    switch extension {
    case JPEG, JPG:
        err = jpeg.Encode(output, img, nil)
        if err != nil {
            return errors.New("Encode失敗")
        }
        fmt.Println("変換成功")
        return nil
    case GIF:
        err = gif.Encode(output, img, nil)
        if err != nil {
            return errors.New("Encode失敗")
        }
        fmt.Println("変換成功")
        return nil
    case PNG:
        err = png.Encode(output, img)
        if err != nil {
            return errors.New("Encode失敗")
        }
        fmt.Println("変換成功")
        return nil
    }
    return nil
}
go.mod
module cvs

go 1.15

とりあえず、エラーハンドリングとかは適当にしつつ、ちゃんと動作するようにしました。

コード解説

今回のコードのテーマとして、
- go mod を使う
- go doc を使う
- パッケージを分ける

というのを決めました。一応、理由としては、
1. go mod により、パッケージの分割を使いやすくする
2. go doc により、ドキュメントを作成することにより、可用性をあげる
この2つがあります。が、今回はパス。

今回使用したパッケージについて、色々書いていきます。

画像の拡張子を変更するCLIコマンドのために使用したパッケージを上げると、

  1. flag
  2. os
  3. path/filepath
  4. image

があります。
それぞれ解説していきます。

flag

ドキュメント
コマンドラインからオプションとして、引数を取得するときに使う

実装したコード(main.go)
var (
    extension string
    imagepath string
    dirpath   string
)
func main() {

    flag.StringVar(&extension, "e", "jpeg", "拡張子の指定")
 // -e "文字列" で、文字列を取得する/デフォルトで"jpeg"が指定されている
    flag.StringVar(&imagepath, "f", "", "変換するファイルのパスの指定")
    flag.StringVar(&dirpath, "d", "", "変換後のファイル名の指定")
    flag.Parse() 
// Parseしないと、変数にパースされない

~~~省略
書き方
flag.StringVar(&変数名, "オプションの記号", "デフォルトの値","このオプションの説明")

文字列の取得以外も取得はできる(はず)

os

ドキュメント
今回は、指定した画像が本当に存在するのか、画像を保存したりに使用しました。

実装したコード(conversion.go)
func FilepathCheck(imagepath string) error {
    switch imagepath {
    case "":
        return errors.New("ファイルの指定がされてません")
    default:
        if f, err := os.Stat(imagepath); os.IsNotExist(err) || f.IsDir() {
            return errors.New("ファイルが存在しません" + imagepath)
        } else {
            return nil
        }
    }
}

//-fで指定したファイルが存在するかを判定している関数です

こいつは結構なんでもできる。

path/filepath

ドキュメント
先程紹介した関数の引数の取得してます。stringからファイルパスを取得してます。

実装したコード(main.go)
f := filepath.Ext(imagepath)
err = conversion.FileExtCheck(f)
//imagepathが本当にあるのか、をチェックする

image

ドキュメント
画像処理系はこいつでできる。

実装したコード(conversion.go)
img, _, Err := image.Decode(exFile)
if Err != nil {
    return errors.New("Decode失敗")
}
//画像をデコードする処理

switch extension {
case JPEG, JPG:
    err = jpeg.Encode(output, img, nil)
    if err != nil {
        return errors.New("Encode失敗")
    }
    fmt.Println("変換成功")
    return nil
case GIF:
    err = gif.Encode(output, img, nil)
    if err != nil {
        return errors.New("Encode失敗")
    }
    fmt.Println("変換成功")
    return nil
case PNG:
    err = png.Encode(output, img)
    if err != nil {
        return errors.New("Encode失敗")
    }
    fmt.Println("変換成功")
    return nil
}
//変換後の拡張子に合わせてエンコードする

画像処理はほぼこいつだけでできるっぽい

まとめ

実際に実行してみると、、、

# go build -o cvs
# ./cvs -e png -f sample.jpeg -d sample.png
変換中・・・
変換成功

って感じで、変換できる。

ちなみに、-d のあとに.pngではなく、適当に書いても、拡張子としては、ちゃんと変換されています。

ブラウザで変換すると、へんなスクリプトを入れられるみたいなのもないし、安全。

やりごたえがあって、楽しかったから、またコマンド作ろうかしら。

では。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1