我以前使用time.Sleep(n)来完成客户端手动取消上传,但是n的值没有很好地确定,并且这种方法不是很优雅。现在我想通过实现io函数来手动调用cancel()。读者界面。下面是客户端代码
package main
import (
"bytes"
"context"
"io"
"log"
"math/rand"
"mime/multipart"
"net/http"
"os"
"time"
)
type CancelReader struct {
cancel func()
offset int32
size int32
flag bool
stop int32
}
func (c CancelReader) Read(p []byte) (n int, err error) {
if c.flag {
return 0, nil
}
log.Printf("Read n %d size %d stop %d offset %d n", n, c.size, c.stop, c.offset)
c.flag = true
if c.offset >= c.size {
return 0, io.EOF
}
n = len(p)
log.Println("N :", n)
str := "0123456789abcdefg"
b := []byte(str)
var res []byte
r := rand.New(rand.NewSource(time.Now().Unix()))
for i := 0; i < n; i++ {
res = append(res, b[r.Intn(len(b))])
}
n = copy(p, res)
c.offset += int32(n)
if c.offset >= c.stop {
c.cancel()
}
c.flag = false
time.Sleep(time.Millisecond * 100)
return n, nil
}
const (
filename = "/Users/jimyag/Downloads/aDrive.dmg"
targetUrl = "http://localhost:9999/upload"
)
func main() {
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
fileWriter, err := bodyWriter.CreateFormFile("uploadfile", filename)
if err != nil {
log.Println("error writing to buffer")
}
fh, err := os.Open(filename)
if err != nil {
log.Println("error opening file")
}
defer fh.Close()
_, err = io.Copy(fileWriter, fh)
if err != nil {
log.Print(err)
}
contentType := bodyWriter.FormDataContentType()
bodyWriter.Close()
cx, cancel := context.WithCancel(context.Background())
cancelReader := CancelReader{
cancel: cancel,
offset: 0,
size: 1024 * 256,
flag: false,
stop: 1024 * 50,
}
req, _ := http.NewRequest(http.MethodPost, targetUrl, cancelReader)
req.Header.Set("Content-Type", contentType)
req = req.WithContext(cx)
_, err = http.DefaultClient.Do(req)
log.Println(err)
}
服务器
package main
import (
"errors"
"io"
"log"
"net/http"
)
func main() {
http.HandleFunc("/upload", func(writer http.ResponseWriter, request *http.Request) {
file, _, err := request.FormFile("uploadfile")
if err != nil {
if request.Context().Err() != nil {
log.Printf("context err %+v", request.Context().Err())
}
log.Printf("err %+v", err)
return
}
p := make([]byte, 10)
defer file.Close()
size := 0
for {
n, err := file.Read(p)
if err != nil {
if errors.Is(err, io.EOF) {
log.Printf("read end")
} else {
log.Printf("read unknow err %+v", err)
}
break
}
size += n
}
log.Printf("read end size %d n", size)
})
err := http.ListenAndServe(":9999", nil)
if err != nil {
log.Printf(err.Error())
}
}
运行如何关闭/中止Golang。客户端过早POST
stdlibtesting
包中有一个鲜为人知的gem:iotest
。
特别是,HalfReader可能是你需要的(或者你可以使用HalfReader并修改它以停止在不同的点。