Skip to content

Kadai3 2 mizushima #68

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
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
2ae7405
課題2を提出
MizushimaToshihiko Jun 11, 2021
68c1603
mainのテストを追加
MizushimaToshihiko Jun 11, 2021
1da407c
コマンドラインオプションの部分を訂正
MizushimaToshihiko Jun 11, 2021
766b979
使い方の2を訂正
MizushimaToshihiko Jun 11, 2021
a56f694
go fmt適用
MizushimaToshihiko Jun 11, 2021
1393b38
README.md それぞれのカバレッジが分かりやすくなるように修正
MizushimaToshihiko Jun 14, 2021
054a7e2
kadai3-1 first commit
MizushimaToshihiko Jun 14, 2021
0738582
とりあえずタイピングと正解判定できるように
MizushimaToshihiko Jun 14, 2021
92b63c2
時間制限とgo.mod追加
MizushimaToshihiko Jun 15, 2021
bcd289c
単語をCSVから読み込むように変更
MizushimaToshihiko Jun 15, 2021
f52c186
モジュール分割
MizushimaToshihiko Jun 15, 2021
0c04595
gigitignore,Makefile,typingのテストを追加,テスト追加に伴いtyping/typing.goとmain.go変更
MizushimaToshihiko Jun 17, 2021
f772cf5
README修正
MizushimaToshihiko Jun 17, 2021
5117bb9
go fmt適用
MizushimaToshihiko Jun 17, 2021
a3f3694
READMEにディレクトリ構成を追加
MizushimaToshihiko Jun 17, 2021
bbe053d
課題2のコミットが残っていたので削除
MizushimaToshihiko Jun 17, 2021
92dd1bc
kadai3-2 first commit
MizushimaToshihiko Jun 18, 2021
9d475ed
オプション周りの修正及びダウンロードはできるように
MizushimaToshihiko Jun 18, 2021
c3b5dea
options/options.goのimport修正、TODOリスト追加
MizushimaToshihiko Jun 18, 2021
274bae4
rangeリクエスト追加
MizushimaToshihiko Jun 21, 2021
3dfddf9
requestの処理を変更、rootフォルダのgo.mod変更
MizushimaToshihiko Jun 21, 2021
a44ebff
ダウンロードの処理を追加、途中
MizushimaToshihiko Jun 21, 2021
d89f81d
並行ダウンロード処理他追加
MizushimaToshihiko Jun 22, 2021
21f4b85
gitignore変更
MizushimaToshihiko Jun 22, 2021
0bb34f4
kadai3-1が残っていたので削除
MizushimaToshihiko Jun 22, 2021
1d024fc
エラー及びキャンセル後の処理の後片づけを修正
MizushimaToshihiko Jun 22, 2021
0b0eb05
gitignore修正
MizushimaToshihiko Jun 22, 2021
b9d9b41
エラーやキャンセルで途中終了した時の処理を修正
MizushimaToshihiko Jun 22, 2021
0ce8d21
go fmt適用、オプションの処理をmain関数に移行
MizushimaToshihiko Jun 23, 2021
886a1a9
エラー処理忘れ修正、errors.Wrapをfmt.Errorfに変更
MizushimaToshihiko Jun 24, 2021
bcbfcb6
テスト追加
MizushimaToshihiko Jun 24, 2021
91a0b2b
downloadのテスト追加
MizushimaToshihiko Jun 25, 2021
7d03be4
download.goのテスト修正途中
MizushimaToshihiko Jun 25, 2021
a3c94f9
go fmt適用
MizushimaToshihiko Jun 25, 2021
5a04cf2
ダウンロードのテスト追加、request.goのテスト追加(途中)
MizushimaToshihiko Jun 28, 2021
14102f5
testの記述を変更
MizushimaToshihiko Jul 6, 2021
1286949
getheader_test.goにt.Helper()追加、request_test.goのTest_Request関数の作成(途中)、…
MizushimaToshihiko Jul 12, 2021
2d3a754
Test_Request関数のコメント追加
MizushimaToshihiko Jul 12, 2021
a31b2bf
README追加、不要なpackage等削除、testdateにフォルダ名変更、それに伴い各テスト関数の修正、Makefileのtest変更他
MizushimaToshihiko Jul 13, 2021
92ba19d
README.md修正
MizushimaToshihiko Jul 13, 2021
2922f3e
README.md修正
MizushimaToshihiko Jul 13, 2021
b36d85e
README.md修正
MizushimaToshihiko Jul 13, 2021
648256b
README.md修正
MizushimaToshihiko Jul 13, 2021
9a40d02
README.md修正
MizushimaToshihiko Jul 13, 2021
b2cfb3a
READEME.md修正、及びdownload.goのコメントのスペルミスを修正
MizushimaToshihiko Jul 14, 2021
880f698
mainのテスト作成中
MizushimaToshihiko Jul 16, 2021
1d54062
main_test.--------止
MizushimaToshihiko Jul 22, 2021
83cb3ef
modified .gitignore
MizushimaToshihiko Jul 30, 2021
3852a4a
modified main function in main.go
MizushimaToshihiko Jul 30, 2021
8c15153
modified main function in main.go
MizushimaToshihiko Jul 30, 2021
548c9aa
modified main.go and download/download.go
MizushimaToshihiko Jul 31, 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 kadai3-1/Mizushima/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bin
1 change: 1 addition & 0 deletions kadai3-2/Mizushima/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bin/*
26 changes: 26 additions & 0 deletions kadai3-2/Mizushima/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
BINARY_NAME := bin/paraDW
GOCMD=go
GOBUILD=$(GOCMD) build
GOCLEAN=$(GOCMD) clean
GOTEST=$(GOCMD) test
GOGET=$(GOCMD) get
GOFMT=$(GOCMD) fmt

all: build test

build:
$(GOBUILD) -o $(BINARY_NAME) -v

test: build
cd download; $(GOTEST) -v -cover
cd getheader; $(GOTEST) -v -cover
cd listen; $(GOTEST) -v -cover
cd request; $(GOTEST) -v -cover

clean:
$(GOCLEAN)

format:
$(GOFMT) ./...

.PHONY: test clean format
83 changes: 83 additions & 0 deletions kadai3-2/Mizushima/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
## 課題3-2 分割ダウンローダを作ろう
- 分割ダウンロードを行う
- Rangeアクセスを用いる
- いくつかのゴルーチンでダウンロードしてマージする
- エラー処理を工夫する
- golang.org/x/sync/errgourpパッケージなどを使ってみる
- キャンセルが発生した場合の実装を行う



### コマンドラインオプション

| ショートオプション | ロングオプション | 説明 | デフォルト |
| --------- | --------- | --------- | --------- |
| -h | --help | 使い方を表示して終了 | - |
| -p \<num> | --procs \<num> | プロセス数を指定 | お使いのPCのコア数 |
| -o \<path> | --output \<path> | ダウンロードしたファイルをどこのディレクトリに保存するか指定する | カレントディレクトリ |
| -t \<num> | --timeout \<num> | サーバーへのリクエストを止める時間を秒数で指定 | 120 |


### インストール方法
```bash
go get github.com/MizushimaToshihiko/gopherdojo-studyroom/kadai3-2/Mizushima
```

### 使い方
1. 実行ファイル作成
```bash
$ make build
```
2. URLを指定してダウンロード実行
```bash
$ ./bin/paraDW [option] URL( URL URL ...)
```

※ URLは複数指定できます
※ ダウンロード先がRangeアクセスに対応していれば、go routineを使った並行ダウンロードを行い、そうでなければ1プロセスのダウンロードを行います

### テストの方法
- バイナリビルド & テスト
```bash
$ make
```
- テスト後の処理(掃除)
```bash
$ make clean
```

### ディレクトリ構成
```bash
.
├── bin
│ └── paraDW # "make build" command required.
├── download
│ ├── download.go
│ ├── download_test.go
│ ├── go.mod
│ └── go.sum
├── getheader
│ ├── geHeader_test.go
│ ├── getHeader.go
│ └── go.mod
├── .gitignore
├── go.mod
├── go.sum
├── listen
│ ├── listen.go
│ └── listen_test.go
├── main.go
├── Makefile
├── README.md
├── request
│ ├── go.mod
│ ├── request.go
│ └── request_test.go
└── testdata
├── 003
└── z4d4kWk.jpg
```

### 参考にしたもの
[pget](https://qiita.com/codehex/items/d0a500ac387d39a34401) (goroutineを使ったダウンロード処理、コマンドラインオプションの処理等々)
https://github.com/gopherdojo/dojo3/pull/50 (ctrl+cを押したときのキャンセル処理など)
157 changes: 157 additions & 0 deletions kadai3-2/Mizushima/download/download.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// download package implements parallel download and non-parallel
// download.
package download

import (
"context"
"fmt"
"io"
"io/ioutil"
"net/url"
"os"
"strconv"

"github.com/MizushimaToshihiko/gopherdojo-studyroom/kadai3-2/Mizushima/request"
"golang.org/x/sync/errgroup"
)

//PDownloader is user-defined struct

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

細かい事ですが、コメントでPDownloaderはユーザ定義型としか言っていないので、他と見比べるとPDownloader自体の説明が入るのが適切なのではと思います。

type PDownloader struct {
url *url.URL // URL for the download
output *os.File // Where to save the downloaded file
fileSize uint // size of the downloaded file
part uint // Number of divided bytes
procs uint // Number of parallel download process
}

// newPDownloader is constructor for PDownloader.
func newPDownloader(url *url.URL, output *os.File, fileSize uint, part uint, procs uint) *PDownloader {
return &PDownloader{
url: url,
output: output,
fileSize: fileSize,
part: part,
procs: procs,
}
}

// Downloader gets elements of PDownloader, the download is parallel or not, temprary
// directory name and context.Context, and drives DownloadFile method if isPara is false
// or PDownload if isPara is true.
//
func Downloader(url *url.URL,
output *os.File, fileSize uint, part uint, procs uint, isPara bool,
tmpDirName string, ctx context.Context) error {
pd := newPDownloader(url, output, fileSize, part, procs)
if !isPara {
fmt.Printf("%s do not accept range access: downloading by single process\n", url)
err := pd.DownloadFile(ctx)
if err != nil {
return err
}
} else {
grp, ctx := errgroup.WithContext(ctx)
if err := pd.PDownload(grp, tmpDirName, procs, ctx); err != nil {
return err
}

if err := grp.Wait(); err != nil {
return err
}
}
return nil
}

// DownloadFile drives a non-parallel download
func (pd *PDownloader) DownloadFile(ctx context.Context) (err error) {

resp, err := request.Request(ctx, "GET", pd.url.String(), "", "")
if err != nil {
return
}
defer func() {
err = resp.Body.Close()
}()

_, err = io.Copy(pd.output, resp.Body)
if err != nil {
return
}

return nil
}

// PDownload drives parallel download. downloaded file is in temporary
// directory named tmpDirName.
func (pd *PDownloader) PDownload(grp *errgroup.Group,
tmpDirName string, procs uint, ctx context.Context) error {
var start, end, idx uint

for idx = uint(0); idx < procs; idx++ {
if idx == 0 {
start = 0
} else {
start = idx*pd.part + 1
}

// if idx is the end
if idx == pd.procs-1 {
end = pd.fileSize
} else {
end = (idx + 1) * pd.part
}

// idxを代入し直す
// https://qiita.com/harhogefoo/items/7ccb4e353a4a01cfa773
idx := idx
// fmt.Printf("start: %d, end: %d, pd.part: %d\n", start, end, pd.part)
bytes := fmt.Sprintf("bytes=%d-%d", start, end)

grp.Go(func() error {
fmt.Printf("grp.Go: tmpDirName: %s, bytes %s, idx: %d\n", tmpDirName, bytes, idx)
return pd.ReqToMakeCopy(tmpDirName, bytes, idx, ctx)
})
}
return nil
}

// ReqToMakeCopy sends a "GET" request with "Range" field with "bytes" range.
// And gets response and make a copy to a temprary file in temprary directory from response body.
//
func (pd *PDownloader) ReqToMakeCopy(tmpDirName, bytes string, idx uint, ctx context.Context) (err error) {
// fmt.Printf("ReqToMakeCopy: tmpDirName: %s, bytes %s, idx: %d\n", tmpDirName, bytes, idx)
resp, err := request.Request(ctx, "GET", pd.url.String(), "Range", bytes)
if err != nil {
return err
}

tmpOut, err := os.Create(tmpDirName + "/" + strconv.Itoa(int(idx)))
if err != nil {
return err
}
// fmt.Printf("tmpOut.Name(): %s\n", tmpOut.Name())
defer func() {
err = tmpOut.Close()
}()

// b := make([]byte, 1000)
// resp.Body.Read(b)
// fmt.Printf("resp.body: %s\n", string(b))

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// fmt.Printf("err: %s\n", err)
if err != io.EOF && err != io.ErrUnexpectedEOF {
return err
}
}

// fmt.Printf("response body: length: %d\n", len(body))

length, err := tmpOut.Write(body)
if err != nil {
return err
}
fmt.Printf("%d/%d was downloaded len=%d\n", idx, pd.procs, length)
return nil
}
Loading