-
Notifications
You must be signed in to change notification settings - Fork 6
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
【課題3】タイピングゲームと分割ダウンローダー #31
base: master
Are you sure you want to change the base?
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,188 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"net/http" | ||
"os" | ||
"path" | ||
"strconv" | ||
|
||
errgroup "golang.org/x/sync/errgroup" | ||
) | ||
|
||
var COUNT = 4 | ||
|
||
func main() { | ||
|
||
url := "https://misc.laboradian.com/test/003" | ||
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. const? |
||
|
||
header, err := HeaderInfo(url) | ||
|
||
if err != nil { | ||
fmt.Println(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. エラーであれば、os.Stderrに出して、os.Exit(1)とかで終了する |
||
} | ||
|
||
var byteRanges []string | ||
if canRangeAccess(header) { | ||
// 分割ダウンロードする | ||
byte_num, err := strconv.Atoi(header["Content-Length"][0]) | ||
if err != nil { | ||
fmt.Println("normal download") | ||
return | ||
} | ||
byteRanges = ByteRanges(byte_num, 2) | ||
} else { | ||
fmt.Println("normal download") | ||
// TODO: 分岐として通常ダウンロード処理を入れる(やるかどうかY/nで聞いてから?) | ||
return | ||
} | ||
|
||
// TODO: tmpディレクトリ作成 | ||
|
||
eg := errgroup.Group{} | ||
for _, range_suffix := range byteRanges { | ||
// 分割ダウンロードする | ||
range_suffix := range_suffix | ||
eg.Go(func() error { | ||
if downloadedFile(url, range_suffix) { | ||
return nil | ||
} | ||
return Download(url, range_suffix) | ||
}) | ||
} | ||
|
||
if err = eg.Wait(); err != nil { | ||
// TODO:標準エラー出力使う | ||
fmt.Fprintf(os.Stderr, "ERROR: %v", err) | ||
return | ||
} | ||
|
||
_, fileName := path.Split(url) | ||
fmt.Println(fileName) | ||
var filePaths []string | ||
for _, suffix := range byteRanges { | ||
filePaths = append(filePaths, path.Join("./tmp/", fileName+"_"+suffix)) | ||
} | ||
// そろっていればtmpファイルを結合する | ||
err = concatFiles(filePaths, "./"+fileName) | ||
|
||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "ERROR: %v", err) | ||
} | ||
|
||
} | ||
|
||
func HeaderInfo(url string) (map[string][]string, error) { | ||
req, _ := http.NewRequest("HEAD", "https://misc.laboradian.com/test/003/", nil) | ||
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. エラー処理 |
||
|
||
client := new(http.Client) | ||
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. この用途なら |
||
resp, err := client.Do(req) | ||
defer resp.Body.Close() | ||
|
||
if err != nil { | ||
fmt.Printf("ioutil err: %v", 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. エラーの表示も立派なエラーハンドリングなので、2重にハンドリングしない。 |
||
return nil, err | ||
} | ||
|
||
// fmt.Println(resp.Header) | ||
return resp.Header, nil | ||
} | ||
|
||
func canRangeAccess(header map[string][]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. http.Headerを使った方が良さそう |
||
v, ok := header["Accept-Ranges"] | ||
|
||
if 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. 早めにreturnしたほうがよい |
||
for _, val := range v { | ||
if val == "bytes" { | ||
if _, ok := header["Content-Length"]; ok { | ||
return true | ||
} | ||
} | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func downloadedFile(url, suffix string) bool { | ||
_, fileName := path.Split(url) | ||
_, err := os.Stat(path.Join("./tmp/", fileName+"_"+suffix)) | ||
return err == nil | ||
} | ||
|
||
func Download(url, byteRange string) error { | ||
|
||
req, _ := http.NewRequest("GET", url, nil) | ||
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. error処理 |
||
req.Header.Set("Range", "bytes="+byteRange) | ||
|
||
client := new(http.Client) | ||
resp, err := client.Do(req) | ||
// TODO: エラー処理する? | ||
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 resp.Body.Close() | ||
|
||
_, fileName := path.Split(url) | ||
fmt.Println(url, byteRange, fileName) | ||
|
||
file, err := os.Create(path.Join("./tmp/", fileName+"_"+byteRange)) | ||
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. ファイルにpathパッケージは使わない。filepathを使う。 |
||
if err != nil { | ||
return err | ||
} | ||
_, err = io.Copy(file, resp.Body) | ||
if closeErr := file.Close(); err == nil { | ||
err = closeErr | ||
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しないのはなぜ? |
||
} | ||
|
||
return err | ||
} | ||
|
||
// ByteRanges は分割したバイト範囲を返す関数です | ||
// ex) 100 で 2分割 0-50, 51-100 | ||
func ByteRanges(length int, parallel_num int) []string { | ||
var byteRanges []string | ||
|
||
for i := 1; i < parallel_num+1; i++ { | ||
byteRanges = append(byteRanges, NthRange(length, parallel_num, i)) | ||
} | ||
return byteRanges | ||
} | ||
|
||
func NthRange(length, parallel_num, n int) string { | ||
bytes_per_file := length / parallel_num | ||
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. 識別子(変数名など)はスネークケースを使わない。キャメルケースを使う。 |
||
if n == 1 { | ||
return "0-" + strconv.Itoa(bytes_per_file) | ||
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するならelseやelse if はいらない |
||
} else if n < parallel_num { | ||
return strconv.Itoa(bytes_per_file*(n-1)+1) + "-" + strconv.Itoa(bytes_per_file*n) | ||
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. fmt.Sprintfを使ったほうがわかりやすそう |
||
} else { | ||
return strconv.Itoa(bytes_per_file*(n-1)+1) + "-" + strconv.Itoa(length) | ||
} | ||
} | ||
|
||
func concatFiles(filePaths []string, fileName string) error { | ||
|
||
var writeBytes [][]byte | ||
|
||
for _, path := range filePaths { | ||
f, err := os.Open(path) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// 一気に全部読み取り | ||
readBytes, err := ioutil.ReadAll(f) | ||
writeBytes = append(writeBytes, readBytes) | ||
|
||
if err := f.Close(); err != nil { | ||
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. Writeじゃないのでエラー処理は不要ではある。 |
||
return err | ||
} | ||
} | ||
|
||
emptyByte := []byte{} | ||
|
||
fmt.Println(fileName) | ||
|
||
err := ioutil.WriteFile(fileName, bytes.Join(writeBytes, emptyByte), 0644) | ||
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 err | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package main | ||
|
||
import ( | ||
"bufio" | ||
"context" | ||
"fmt" | ||
"io" | ||
"math/rand" | ||
"os" | ||
"time" | ||
) | ||
|
||
func main() { | ||
|
||
var counter int | ||
ch := input(os.Stdin) | ||
|
||
words, err := RegisterWords() | ||
if err != nil { | ||
fmt.Println("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. エラーメッセージは丁寧に書く。 |
||
} | ||
|
||
wordNum := len(words) | ||
rand.Seed(time.Now().UnixNano()) | ||
|
||
var displayWord string | ||
|
||
displayWord = words[rand.Intn(wordNum)] | ||
fmt.Print(displayWord, " : ") | ||
|
||
bc := context.Background() | ||
t := 10 * time.Second | ||
ctx, cancel := context.WithTimeout(bc, t) | ||
defer cancel() | ||
|
||
for { | ||
select { | ||
case v := <-ch: | ||
fmt.Println(v, "come") | ||
if v == displayWord { | ||
counter += 1 | ||
fmt.Println("o") | ||
} else { | ||
fmt.Println("x") | ||
} | ||
displayWord = words[rand.Intn(wordNum)] | ||
fmt.Print(displayWord, " : ") | ||
case <-ctx.Done(): | ||
fmt.Println("\ntime up!\nscore :", counter) | ||
goto finish | ||
} | ||
} | ||
finish: | ||
} | ||
|
||
func RegisterWords() ([]string, error) { | ||
f, err := os.Open("list.csv") | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var words []string | ||
|
||
scanner := bufio.NewScanner(f) | ||
for scanner.Scan() { | ||
words = append(words, scanner.Text()) | ||
} | ||
err = f.Close() | ||
return words, err | ||
} | ||
|
||
func input(r io.Reader) chan string { | ||
c := make(chan string) | ||
go func() { | ||
s := bufio.NewScanner(r) | ||
for s.Scan() { | ||
c <- s.Text() | ||
} | ||
close(c) | ||
}() | ||
return c | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
apple | ||
orange | ||
wood | ||
tree | ||
flower |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package main | ||
|
||
import ( | ||
"bufio" | ||
"context" | ||
"fmt" | ||
"io" | ||
"math/rand" | ||
"os" | ||
"time" | ||
) | ||
|
||
func main() { | ||
|
||
var counter int | ||
ch := input(os.Stdin) | ||
|
||
words, err := RegisterWords() | ||
if err != nil { | ||
fmt.Println("err") | ||
} | ||
|
||
wordNum := len(words) | ||
rand.Seed(time.Now().UnixNano()) | ||
|
||
var displayWord string | ||
|
||
displayWord = words[rand.Intn(wordNum)] | ||
fmt.Print(displayWord, " : ") | ||
|
||
bc := context.Background() | ||
t := 10 * time.Second | ||
ctx, cancel := context.WithTimeout(bc, t) | ||
defer cancel() | ||
|
||
for { | ||
select { | ||
case v := <-ch: | ||
fmt.Println(v, "come") | ||
if v == displayWord { | ||
counter += 1 | ||
fmt.Println("o") | ||
} else { | ||
fmt.Println("x") | ||
} | ||
displayWord = words[rand.Intn(wordNum)] | ||
fmt.Print(displayWord, " : ") | ||
case <-ctx.Done(): | ||
fmt.Println("\ntime up!\nscore :", counter) | ||
goto finish | ||
} | ||
} | ||
finish: | ||
} | ||
|
||
func RegisterWords() ([]string, error) { | ||
f, err := os.Open("list.csv") | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var words []string | ||
|
||
scanner := bufio.NewScanner(f) | ||
for scanner.Scan() { | ||
words = append(words, scanner.Text()) | ||
} | ||
err = f.Close() | ||
return words, err | ||
} | ||
|
||
func input(r io.Reader) chan string { | ||
c := make(chan string) | ||
go func() { | ||
s := bufio.NewScanner(r) | ||
for s.Scan() { | ||
c <- s.Text() | ||
} | ||
close(c) | ||
}() | ||
return c | ||
} |
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.
定数であれば、constで先頭を大文字にするとパッケージ外に公開されます