Skip to content

Kadai3 2/yuonoda #22

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

Closed
wants to merge 94 commits into from
Closed
Show file tree
Hide file tree
Changes from 73 commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
147b8c4
画像変換の最小の処理をつくった
Nov 15, 2020
c9131d0
ディレクトリを変更
Dec 6, 2020
3753ee4
flagで変換前後のフォーマットを受け取れるようにした
Dec 6, 2020
7347ed5
Gif画像への変換を追加
Dec 6, 2020
0d10587
ディレクトリ内の画像を取得する関数を追加
Dec 7, 2020
87005e9
画像パスのスライスを返すように変更
Dec 7, 2020
e1d1691
複数の画像フォーマットをデコードする関数を作成
Dec 8, 2020
7c297c7
ファイル出力をまとめた
Dec 8, 2020
459b199
複数フォーマットをエンコードする関数を作成
Dec 8, 2020
52a0d06
画像パスをループさせて一括変換
Dec 8, 2020
53836db
mainパッケージと自作パッケージを分離
Dec 8, 2020
4770892
ファイル名の比較を修正
Dec 9, 2020
dc4cf11
go.modファイルを作成
Dec 9, 2020
829dcc1
パッケージ名を変更
Dec 13, 2020
1c212a6
Merge pull request #1 from yuonoda/kadai1-yuonoda
Dec 13, 2020
6d3e221
typoを修正
Dec 13, 2020
06c6c58
Merge pull request #2 from yuonoda/kadai1-yuonoda
Dec 13, 2020
daf05ef
実行ファイルをつくり、モジュールの設定を追加
Dec 13, 2020
cad2538
ユーザー定義型imgを追加
Dec 13, 2020
e730f5b
go docを追加
Dec 13, 2020
8929c28
実行ファイルを更新
Dec 13, 2020
4663590
moduleを整理
Dec 14, 2020
f73eeaa
フォーマット判定のcase文を整理
Dec 14, 2020
dd3a43c
sliceが空のときの返り値を修正
Dec 14, 2020
ca648d2
fmtをimgFmtに修正
Dec 14, 2020
e0e15d6
課題2-1作成
Dec 20, 2020
2f3feef
課題2-1を完了
Dec 22, 2020
7145e14
テストファイルを作成
Dec 22, 2020
6942e82
Merge remote-tracking branch 'origin/kadai1-yuonoda'
yuonoda Jan 9, 2021
d1738bb
エンコードのテストを作成
yuonoda Jan 9, 2021
8b34dad
imgConverterを修正
yuonoda Jan 11, 2021
1e1ea93
エンコードのテストを作成
yuonoda Jan 11, 2021
afc0e5c
imgConverterのテストを作成
yuonoda Jan 12, 2021
6d89a8f
デコードをリファクタ
yuonoda Jan 12, 2021
a90bcb8
全体のテストを追加
yuonoda Jan 12, 2021
e2a5d1a
到達しないreturnを削除
yuonoda Jan 12, 2021
dce4bcf
コメントを修正
yuonoda Jan 12, 2021
43d47ae
ファイル名を修正
yuonoda Jan 12, 2021
ad81dd3
コメントを修正
yuonoda Jan 12, 2021
5160bd3
バイナリを削除
yuonoda Jan 13, 2021
ec92022
テストパッケージを分離
yuonoda Jan 13, 2021
46261a4
errの変数名を変更
yuonoda Jan 14, 2021
c43f0d4
flagの処理をライブラリ外に出す
yuonoda Jan 14, 2021
5968420
テーブル駆動テスト内をサブテスト化
yuonoda Jan 14, 2021
db486ea
テストディレクトリ作成用の関数を修正中
yuonoda Jan 14, 2021
2f90502
テストディレクトリの構造をパラメーター化
yuonoda Jan 14, 2021
3bfc3fd
エラーケースの判定を真偽値にする
yuonoda Jan 15, 2021
f9c6b1f
変更をリセット
yuonoda Jan 17, 2021
f72c945
ファイルを作成
yuonoda Jan 17, 2021
58a1dee
モジュールを修正
yuonoda Jan 17, 2021
5c9fc6f
モジュールを修正
yuonoda Jan 17, 2021
a44ac58
モジュールを修正
yuonoda Jan 17, 2021
4ef1efb
GETリクエスト実装
yuonoda Jan 17, 2021
d51871b
ダウンロードしたデータをファイルに書き込み
yuonoda Jan 18, 2021
63e4494
HEADでコンテンツサイズを取得
yuonoda Jan 18, 2021
ba5b36e
プライベートメソッドをエクスポートする
yuonoda Jan 19, 2021
52e70e6
逐次アクセスでまず実装
yuonoda Jan 19, 2021
6f54f5b
Rangeの指定を修正
yuonoda Jan 20, 2021
ce69308
Rangeリクエストを別関数にした
yuonoda Jan 21, 2021
b0e4086
バイト列の一部を置換する関数を作成
yuonoda Jan 22, 2021
3766296
マージする処理を追加
yuonoda Jan 22, 2021
696f186
URLとシングルサイズを引数にした
yuonoda Jan 24, 2021
2e5f465
範囲の上限を設定
yuonoda Jan 24, 2021
9008fab
ステータスコードを判定
yuonoda Jan 24, 2021
b450978
リトライ機能を追加
yuonoda Jan 24, 2021
fa14393
エラーグループを使ってエラー処理
yuonoda Jan 24, 2021
d9c9f53
エラーが起きたらファイルを削除
yuonoda Jan 24, 2021
8c017ad
スリープをランダムにした
yuonoda Jan 24, 2021
d564a48
テストを別パッケージにした
yuonoda Jan 24, 2021
8be759c
型とメソッドにまとめた
yuonoda Jan 25, 2021
925d11c
resourceに処理を分離
yuonoda Jan 26, 2021
7acb86e
Runのテストを追加
yuonoda Jan 26, 2021
9ee4f5c
readmeを削除
yuonoda Jan 26, 2021
d5f70d4
Resourceメソッド内のlog.Fatalを削除
yuonoda Jan 31, 2021
7062317
テスト内のlog.Fatalを削除
yuonoda Jan 31, 2021
fd0eff9
リトライ数を定数にした
yuonoda Jan 31, 2021
28d355c
ダウンロード先のデフォルトをカレントディレクトリに変更した
yuonoda Jan 31, 2021
1aaaa42
中断時にキャンセルコンテクストにいれる
yuonoda Feb 1, 2021
d326a56
中断時にBodyの読み込みをやめる処理を追加
yuonoda Feb 1, 2021
a34e5bd
TODOを整理
yuonoda Feb 1, 2021
b5c977a
パッケージの名称を変更
yuonoda Feb 2, 2021
e59e2da
utilitiesを別パッケージに分離
yuonoda Feb 2, 2021
362adda
TODOを整理
yuonoda Feb 2, 2021
9fd8299
Downloadをメソッドにした
yuonoda Feb 2, 2021
4b8e35b
terminateを別パッケージにした
yuonoda Feb 2, 2021
e70df58
一時ファイルの削除をdeferで一括化
yuonoda Feb 3, 2021
e87a1b9
Download関数のエラー時にlog.Fatalを呼び出し
yuonoda Feb 3, 2021
6cadba5
新しいdownloaderパッケージ用にテストを修正
yuonoda Feb 3, 2021
cffce87
terminateパッケージのテストを追加
yuonoda Feb 3, 2021
26bebfe
Body読み込み時のGoルーチンを削除
yuonoda Feb 3, 2021
9f0a7a3
エラーハンドリング漏れを修正
yuonoda Feb 7, 2021
18f9d15
リクエストのエラーをエラーチャンネルで制御
yuonoda Feb 7, 2021
c1d2419
deferのエラーハンドリングを追加
yuonoda Feb 7, 2021
e72a1dc
一時ファイルが残っってなければ、削除をスキップ
yuonoda Feb 7, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.idea
19 changes: 0 additions & 19 deletions README.md

This file was deleted.

5 changes: 5 additions & 0 deletions kadai3-2/yuonoda/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/yuonoda/gopherdojo-studyroom/kadai3-2/yuonoda

go 1.15

require golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
2 changes: 2 additions & 0 deletions kadai3-2/yuonoda/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
3 changes: 3 additions & 0 deletions kadai3-2/yuonoda/lib/export_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package splitDownload

var ExportedFillByteArr = fillByteArr
157 changes: 157 additions & 0 deletions kadai3-2/yuonoda/lib/resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package splitDownload

import (
"bytes"
"errors"
"fmt"
"golang.org/x/sync/errgroup"
"io/ioutil"
"log"
"math"
"math/rand"
"net/http"
"strconv"
"time"
)

type partialContent struct {
StartByte int
EndByte int
Body []byte
}

type resource struct {
Url string
Size int
BatchSize int
BatchCount int
Content []byte
PartialContentCh chan partialContent
Http http.Client
}

func (r *resource) GetSize() error {
log.Printf("resource.getSize()\n")

// HEADでサイズを調べる
res, err := r.Http.Head(r.Url)
if err != nil {
return err
}

// データサイズを取得
header := res.Header
cl, ok := header["Content-Length"]
if !ok {
return errors.New("Content-Length couldn't be found")
}
r.Size, err = strconv.Atoi(cl[0])
if err != nil {
return err
}
return nil

}

func (r *resource) GetPartialContent(startByte int, endByte int) error {
log.Printf("resource.getPartialContent(%d, %d)\n", startByte, endByte)
// Rangeヘッダーを作成
rangeVal := fmt.Sprintf("bytes=%d-%d", startByte, endByte)

// リクエストとクライアントの作成
reader := bytes.NewReader([]byte{})
req, err := http.NewRequest("GET", r.Url, reader)
if err != nil {
return err
}
req.Header.Set("Range", rangeVal)
client := &http.Client{}

res := &http.Response{}
for i := 0; i < 3; i++ {
// リクエストの実行
log.Printf("rangeVal[%d]:%s", i, rangeVal)
res, err = client.Do(req)
if err != nil {
return err
}

// ステータスが200系ならループを抜ける
log.Printf("res.StatusCode:%d\n", res.StatusCode)
if res.StatusCode >= 200 && res.StatusCode <= 299 {
break
}

// 乱数分スリープ
rand.Seed(time.Now().UnixNano())
randFloat := rand.Float64() + 1
randMs := math.Pow(randFloat, float64(i+1)) * 1000
sleepTime := time.Duration(randMs) * time.Millisecond
log.Printf("sleep:%v\n", sleepTime)
time.Sleep(sleepTime)
}

// 正常系レスポンスでないとき
if res.StatusCode < 200 || res.StatusCode > 299 {
return errors.New("status code is not 2xx, got " + res.Status)
}

// bodyの取得
body, err := ioutil.ReadAll(res.Body)
defer res.Body.Close()
if err != nil {
return err
}

pc := partialContent{StartByte: startByte, EndByte: endByte, Body: body}
r.PartialContentCh <- pc
return nil
}

func (r *resource) GetContent(batchCount int) error {
log.Println("resource.getContent()")

// コンテンツのデータサイズを取得
err := r.GetSize()
if err != nil {
log.Fatal(err)
}
log.Printf("r.size: %d\n", r.Size)

// batchCount分リクエスト
r.BatchCount = batchCount
r.BatchSize = int(math.Ceil(float64(r.Size) / float64(r.BatchCount)))
r.Content = make([]byte, r.Size)
var eg errgroup.Group
r.PartialContentCh = make(chan partialContent, r.BatchCount)
for i := 0; i < r.BatchCount; i++ {

// 担当する範囲を決定
startByte := r.BatchSize * i
endByte := r.BatchSize*(i+1) - 1
if endByte > r.Size {
endByte = r.Size
}

// レンジごとにリクエスト
eg.Go(func() error {
return r.GetPartialContent(startByte, endByte)
})
}

// 1リクエストでも失敗すれば終了
if err := eg.Wait(); err != nil {
return err
}

// 一つのバイト列にマージ
r.Content = make([]byte, r.Size)
for i := 0; i < r.BatchCount; i++ {
log.Println("merging...")
pc := <-r.PartialContentCh
log.Printf("pc.startByte: %v\n", pc.StartByte)
fillByteArr(r.Content[:], pc.StartByte, pc.Body)
}

return nil
}
48 changes: 48 additions & 0 deletions kadai3-2/yuonoda/lib/splitDownload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package splitDownload

import (
"log"
"os"
"path/filepath"
"strings"
)

func Run(url string, batchCount int, dwDirPath string) string {
log.Println("Run")

// ファイルの作成
_, filename := filepath.Split(url)
homedir, err := os.UserHomeDir()
if err != nil {
log.Fatal(err)
}
if dwDirPath == "" {
dwDirPath = homedir + "/Downloads"
}
dwFilePath := dwDirPath + "/" + filename + ".download"
log.Println(dwFilePath)
dwFile, err := os.Create(dwFilePath)
if err != nil {
os.Remove(dwFilePath)
log.Fatal(err)
}

// ダウンロード実行
r := &resource{Url: url}
err = r.GetContent(batchCount)
if err != nil {
log.Fatal(err)
}

// データの書き込み
_, err = dwFile.Write(r.Content)
if err != nil {
os.Remove(dwFilePath)
log.Fatal(err)
}
finishedFilePath := strings.Trim(dwFilePath, ".download")
os.Rename(dwFilePath, finishedFilePath)

log.Println("download succeeded!")
return finishedFilePath
}
98 changes: 98 additions & 0 deletions kadai3-2/yuonoda/lib/splitDownload_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package splitDownload_test

import (
"log"
"os"
"reflect"
"testing"

splitDownload "github.com/yuonoda/gopherdojo-studyroom/kadai3-2/yuonoda/lib"
)

func TestRun(t *testing.T) {
cases := []struct {
name string
url string
expectedSize int64
concurrency int
}{
{
name: "basic",
url: "https://dumps.wikimedia.org/jawiki/20210101/jawiki-20210101-pages-articles-multistream-index.txt.bz2",
expectedSize: int64(25802009),
concurrency: 3,
},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
// ダウンロードパスを指定
homedir, err := os.UserHomeDir()
if err != nil {
log.Fatal(err)
}
dwDirPath := homedir + "/Downloads"

// ダウンロード
filePath := splitDownload.Run(c.url, c.concurrency, dwDirPath)
file, err := os.Open(filePath)
if err != nil {
t.Error(err)
}

// サイズを取得
info, err := file.Stat()
if err != nil {
t.Error(err)
}
dwSize := info.Size()

// サイズを比較
if dwSize != c.expectedSize {
t.Errorf("Size doesn't match, got %d but expexted %d", dwSize, c.expectedSize)
}

// ファイルを削除
err = os.Remove(filePath)
if err != nil {
t.Error(err)
}
})
}

}

func TestFillByteArr(t *testing.T) {
cases := []struct {
name string
arr []byte
startAt int
partArr []byte
expectedArr []byte
}{
{
name: "basic",
arr: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
startAt: 3,
partArr: []byte{4, 5, 6},
expectedArr: []byte{0, 0, 0, 4, 5, 6, 0, 0, 0, 0},
},
{
name: "basic2",
arr: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
startAt: 8,
partArr: []byte{9, 10},
expectedArr: []byte{0, 0, 0, 0, 0, 0, 0, 0, 9, 10},
},
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
splitDownload.ExportedFillByteArr(c.arr[:], c.startAt, c.partArr)
if !reflect.DeepEqual(c.expectedArr, c.arr) {
t.Error("Array does not match")
}
})
}

}
9 changes: 9 additions & 0 deletions kadai3-2/yuonoda/lib/utilities.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package splitDownload

// 配列の一部を別配列に置き換える
func fillByteArr(arr []byte, startAt int, partArr []byte) {
for i := 0; i < len(partArr); i++ {
globalIndex := i + startAt
arr[globalIndex] = partArr[i]
}
}
15 changes: 15 additions & 0 deletions kadai3-2/yuonoda/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package main

import (
"flag"
splitDownload "github.com/yuonoda/gopherdojo-studyroom/kadai3-2/yuonoda/lib"
)

var url = flag.String("url", "https://dumps.wikimedia.org/jawiki/20210101/jawiki-20210101-pages-articles-multistream-index.txt.bz2", "URL to download")
var batchCount = flag.Int("c", 1, "how many times you request content")
var dwDirPath = flag.String("path", "", "where to put a downloaded file")

func main() {
flag.Parse()
splitDownload.Run(*url, *batchCount, *dwDirPath)
}