-
Notifications
You must be signed in to change notification settings - Fork 2
Kadai2 simady #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Kadai2 simady #16
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
## 概要 | ||
指定ディレクトリ配下にある画像ファイルを、再帰的に走査して別の形式のファイルに変換します。png, jpeg, gifに対応しています。 | ||
|
||
## 実行方法 | ||
|
||
``` | ||
go run main.go -in TARGET_PATH -src SOURCE_EXTENSION -dst DESTINATION_EXTENSION | ||
|
||
-in 走査したいディレクトリを指定します。 | ||
-src 変換元のファイル形式を指定します。 | ||
-dst 変換後のファイル形式を指定します。 | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package converter | ||
|
||
import ( | ||
"image" | ||
"log" | ||
"os" | ||
) | ||
|
||
// IConverter コンバーターインターフェース | ||
type IConverter interface { | ||
convert(file *os.File, img image.Image) error | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 型名がexportされているのにメソッド名がexportされていない理由は? |
||
validate() bool | ||
} | ||
|
||
type converter struct { | ||
src, dst string | ||
} | ||
|
||
var ( | ||
convertibleExts = []string{"jpeg", "png", "gif"} | ||
) | ||
|
||
// Validate フィールドのバリデーション処理を行う. | ||
func (c converter) validate() bool { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 構造体の場合、特に理由がない場合はレシーバにはポインタを使う。 |
||
return validate(c.src) && validate(c.dst) | ||
} | ||
|
||
func validate(ext string) bool { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. なぜメソッドでやらずに関数で行う? |
||
for _, e := range convertibleExts { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mapを使えば関数不要そう。 |
||
if ext == e { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
// Convert 画像フォーマットの変換を行う. | ||
func Convert(path string, c IConverter, fileName string) error { | ||
|
||
src, err := os.Open(path) | ||
if err != nil { | ||
log.Fatalf("Faild to open file. err = %v\n", err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
defer src.Close() | ||
|
||
var img image.Image | ||
img, _, err = image.Decode(src) | ||
if err != nil { | ||
log.Fatalf("Faild to decode file. err = %v\n", err) | ||
} | ||
|
||
var dst *os.File | ||
dst, err = os.Create(fileName) | ||
if err != nil { | ||
log.Fatalf("Faild to create file. err = %v\n", err) | ||
} | ||
defer dst.Close() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 書き込み用のファイルは閉じる際にエラー処理をする。 |
||
|
||
return c.convert(dst, img) | ||
} | ||
|
||
// GetConverter 拡張子に対応したコンバーターを取得する. | ||
func GetConverter(src, dst string) IConverter { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Goでは |
||
c := converter{src: src, dst: dst} | ||
c.validate() | ||
switch dst { | ||
case "png": | ||
return pngConverter{c} | ||
case "jpeg": | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
return jpegConverter{c} | ||
case "gif": | ||
return gifConverter{c} | ||
default: | ||
log.Fatalf("Unsupported extension. dst = %v\n", dst) | ||
} | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package converter | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
func TestGetConverter(t *testing.T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. テーブル駆動テストをする |
||
var c IConverter | ||
c = GetConverter("jpeg", "png") | ||
if _, ok := c.(pngConverter); !ok { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. せっかくインタフェースにしているのに、テストが具体的な実装に依存しているのはあまりよくない。 |
||
t.Fatal("c is not pngConverter.") | ||
} | ||
|
||
c = GetConverter("png", "gif") | ||
if _, ok := c.(gifConverter); !ok { | ||
t.Fatal("c is not gifConverter.") | ||
} | ||
|
||
c = GetConverter("gif", "jpeg") | ||
if _, ok := c.(jpegConverter); !ok { | ||
t.Fatal("c is not jpegConverter.") | ||
} | ||
} | ||
|
||
func TestValidate(t *testing.T) { | ||
var src, dst string | ||
|
||
// 正常系 | ||
src, dst = "jpeg", "png" | ||
if !(converter{src: src, dst: dst}).validate() { | ||
t.Fatalf("Expect src=%v dst=%v to be supported.\n", src, dst) | ||
} | ||
|
||
src, dst = "png", "gif" | ||
if !(converter{src: src, dst: dst}).validate() { | ||
t.Fatalf("Expect src=%v dst=%v to be supported.\n", src, dst) | ||
} | ||
|
||
src, dst = "gif", "jpeg" | ||
if !(converter{src: src, dst: dst}).validate() { | ||
t.Fatalf("Expect src=%v dst=%v to be supported.\n", src, dst) | ||
} | ||
|
||
// 異常系 | ||
src, dst = "png", "webp" | ||
if (converter{src: src, dst: dst}).validate() { | ||
t.Fatalf("src=%v dst=%v not supported.\n", src, dst) | ||
} | ||
} | ||
|
||
func TestConvert(t *testing.T) { | ||
var path, fileName string | ||
path, fileName = "../src/gopher.jpeg", "../dst/test/test.png" | ||
if err := Convert(path, pngConverter{}, fileName); err != nil { | ||
t.Fatal("Failed to convert.") | ||
} | ||
|
||
path, fileName = "../src/gopher.png", "../dst/test/test.gif" | ||
if err := Convert(path, gifConverter{}, fileName); err != nil { | ||
t.Fatal("Failed to convert.") | ||
} | ||
|
||
path, fileName = "../src/gopher.gif", "../dst/test/test.jpeg" | ||
if err := Convert(path, jpegConverter{}, fileName); err != nil { | ||
t.Fatal("Failed to convert.") | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package converter | ||
|
||
import ( | ||
"image" | ||
"image/gif" | ||
"os" | ||
) | ||
|
||
// gifConverter Gif用コンバーター | ||
type gifConverter struct { | ||
converter | ||
} | ||
|
||
func (gc gifConverter) convert(file *os.File, img image.Image) error { | ||
return gif.Encode(file, img, &gif.Options{NumColors: 256}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package converter | ||
|
||
import ( | ||
"image" | ||
"image/jpeg" | ||
"os" | ||
) | ||
|
||
// jpegConverter Jpeg用コンバーター | ||
type jpegConverter struct { | ||
converter | ||
} | ||
|
||
func (jc jpegConverter) convert(file *os.File, img image.Image) error { | ||
return jpeg.Encode(file, img, &jpeg.Options{Quality: 100}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package converter | ||
|
||
import ( | ||
"image" | ||
"image/png" | ||
"os" | ||
) | ||
|
||
// pngConverter Png用コンバーター | ||
type pngConverter struct { | ||
converter | ||
} | ||
|
||
func (pc pngConverter) convert(file *os.File, img image.Image) error { | ||
return png.Encode(file, img) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module imgconv | ||
|
||
go 1.12 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"log" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
|
||
"imgconv/converter" | ||
) | ||
|
||
var ( | ||
in, out, src, dst string | ||
) | ||
|
||
func init() { | ||
flag.StringVar(&in, "in", "", "target directory.") | ||
flag.StringVar(&src, "src", "jpeg", "source extension. [jpeg,png]") | ||
flag.StringVar(&dst, "dst", "png", "destination extension. [jpeg,png]") | ||
} | ||
|
||
func main() { | ||
flag.Parse() | ||
c := converter.GetConverter(src, dst) | ||
err := filepath.Walk(in, func(path string, info os.FileInfo, err error) error { | ||
ext := filepath.Ext(path) | ||
if ext != "."+src { | ||
return nil | ||
} | ||
out = "dst/" + strings.ReplaceAll(filepath.Base(path), ext, "."+dst) | ||
return converter.Convert(path, c, out) | ||
}) | ||
if err != nil { | ||
log.Fatalf("Error occurred in convert process. err = %v\n", err) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
## 概要 | ||
指定ディレクトリ配下にある画像ファイルを、再帰的に走査して別の形式のファイルに変換します。 | ||
png, jpeg, gifに対応しています。 | ||
|
||
## 実行方法 | ||
|
||
``` | ||
imgconv -in INPUT_PATH -out OUTPUT_PATH -src SOURCE_EXTENSION -dst DESTINATION_EXTENSION | ||
|
||
-in 走査したいディレクトリを指定します。 | ||
-out 出力先のディレクトリを指定します。 | ||
-src 変換元のファイル形式を指定します。 | ||
-dst 変換後のファイル形式を指定します。 | ||
``` | ||
|
||
## テストカバレッジ | ||
``` | ||
$ go test -cover imgconv/... | ||
ok imgconv 0.434s coverage: 88.2% of statements | ||
ok imgconv/converter 0.430s coverage: 100.0% of statements | ||
``` | ||
|
||
### io.ReaderとioWriterについて | ||
``` | ||
type Reader interface { | ||
Read(p []byte) (n int, err error) | ||
} | ||
``` | ||
byte型のスライスを引数に取り、pに値を読み込む。 | ||
読み込んだバイト数とエラーを返す。 | ||
|
||
``` | ||
type Writer interface { | ||
Write(p []byte) (n int, err error) | ||
} | ||
``` | ||
byte型のスライスを引数に取り、pから値を書き込む。 | ||
書き込んだバイト数とエラーを返す。 | ||
|
||
- 標準パッケージでどのように使われているか | ||
- tarやzip等のアーカイブ形式 | ||
- bzip2やlzw等の圧縮形式 | ||
- ECDSAやtls等の暗号化形式 | ||
- base64や16進数等のフォーマット | ||
- gifやpng等の画像形式 | ||
- 標準入出力や標準エラー出力 | ||
- http通信でのリクレス等 | ||
- MIMEマルチパートやQuoted-printable等の方式 | ||
- ファイル | ||
- 文字列やバイトバッファ | ||
- ログ | ||
|
||
上記のように様々な箇所での読み書きに使用されていた。 | ||
また、以下のようなerrorの戻り値を利用するような使い方も見られた。 | ||
``` | ||
type errorReader struct { | ||
error | ||
} | ||
func (r errorReader) Read(p []byte) (n int, err error) { | ||
return 0, r.error | ||
} | ||
``` | ||
|
||
``` | ||
type eofReader struct{} | ||
func (eofReader) Read([]byte) (int, error) { | ||
return 0, EOF | ||
} | ||
``` | ||
|
||
- io.Readerとio.Writerがあることでどういう利点があるのか具体例を挙げて考えてみる | ||
- io.Readerやio.Writerを実装したインスタンスをインターフェースを介して利用することで、使用する側は実体が何かを意識せずに扱うことができる。 | ||
- シンプルなインターフェースなので実装が容易。何に対して読み書きを行うかは定義されていないので、入出力先に応じて適宜実装すれば良い。 | ||
- テストやデバッグ時に、実際の処理とは別の処理に置き換えるといった差し替えが容易に行える。 | ||
- ioパッケージのpipeやio/ioutilパッケージで定義された既存の便利メソッドを利用できる。 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package converter | ||
|
||
import ( | ||
"fmt" | ||
"image" | ||
"os" | ||
) | ||
|
||
// IConverter コンバーターインターフェース | ||
type IConverter interface { | ||
Convert(path string, fileName string) error | ||
} | ||
|
||
type converter struct { | ||
Encoder | ||
} | ||
|
||
type Encoder interface { | ||
encode(file *os.File, img image.Image) error | ||
} | ||
|
||
// NewConverter 拡張子に対応したコンバーターを生成する. | ||
func NewConverter(dst string) (IConverter, error) { | ||
switch dst { | ||
case "png": | ||
return converter{&pngEncoder{}}, nil | ||
case "jpg": | ||
return converter{&jpgEncoder{}}, nil | ||
case "gif": | ||
return converter{&gifEncoder{}}, nil | ||
} | ||
return nil, ImgconvError{Message: fmt.Sprintf("Unsupported extension. dst = %v\n", dst)} | ||
} | ||
|
||
// Convert 画像フォーマットの変換を行う. | ||
func (c converter) Convert(path string, fileName string) error { | ||
src, err := os.Open(path) | ||
if err != nil { | ||
return err | ||
} | ||
defer src.Close() | ||
|
||
var img image.Image | ||
img, _, err = image.Decode(src) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
var dst *os.File | ||
dst, err = os.Create(fileName) | ||
if err != nil { | ||
return err | ||
} | ||
defer dst.Close() | ||
|
||
return c.encode(dst, img) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Goでは先頭に
I
とつける習慣はない。