github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/zhttp/response.go (about) 1 package zhttp 2 3 import ( 4 "bufio" 5 "bytes" 6 "encoding/json" 7 "encoding/xml" 8 "io" 9 "io/ioutil" 10 "net/http" 11 "os" 12 "strings" 13 "time" 14 15 "github.com/sohaha/zlsgo/zfile" 16 "github.com/sohaha/zlsgo/zjson" 17 "golang.org/x/net/html/charset" 18 ) 19 20 type Res struct { 21 err error 22 r *Engine 23 req *http.Request 24 resp *http.Response 25 client *http.Client 26 *multipartHelper 27 downloadProgress DownloadProgress 28 tmpFile string 29 requesterBody []byte 30 responseBody []byte 31 cost time.Duration 32 } 33 34 func (r *Res) Request() *http.Request { 35 return r.req 36 } 37 38 func (r *Res) Response() *http.Response { 39 return r.resp 40 } 41 42 func (r *Res) StatusCode() int { 43 if r == nil || r.resp == nil { 44 return 0 45 } 46 _, _ = r.ToBytes() 47 return r.resp.StatusCode 48 } 49 50 func (r *Res) GetCookie() map[string]*http.Cookie { 51 cookiesRaw := r.Response().Cookies() 52 cookies := make(map[string]*http.Cookie, len(cookiesRaw)) 53 var cookie *http.Cookie 54 for i := range cookiesRaw { 55 if cookie = cookiesRaw[i]; cookie != nil { 56 cookies[cookie.Name] = cookie 57 } 58 } 59 return cookies 60 } 61 62 func (r *Res) Bytes() []byte { 63 data, _ := r.ToBytes() 64 return data 65 } 66 67 func (r *Res) Stream(fn func(line []byte, eof bool) error) error { 68 if r.err != nil || r.resp == nil { 69 return r.err 70 } 71 r.responseBody = nil 72 defer r.resp.Body.Close() 73 br := bufio.NewReader(r.resp.Body) 74 for { 75 bs, err := br.ReadBytes('\n') 76 77 if err != nil && err != io.EOF { 78 return err 79 } 80 81 if err := fn(bs, err == io.EOF); err != nil { 82 return err 83 } 84 85 if err == io.EOF { 86 break 87 } 88 } 89 90 return nil 91 } 92 93 func (r *Res) ToBytes() ([]byte, error) { 94 if r.err != nil || r.resp == nil { 95 return nil, r.err 96 } 97 if r.responseBody != nil { 98 return r.responseBody, nil 99 } 100 defer r.resp.Body.Close() 101 respBody, err := ioutil.ReadAll(r.resp.Body) 102 _, _ = io.Copy(ioutil.Discard, r.resp.Body) 103 if err != nil { 104 r.err = err 105 return nil, err 106 } 107 108 r.responseBody = forceUTF8(r, respBody) 109 return r.responseBody, nil 110 } 111 112 func forceUTF8(r *Res, respBody []byte) []byte { 113 ctype := r.resp.Header.Get("Content-Type") 114 c, n, _ := charset.DetermineEncoding(respBody, ctype) 115 if n != "utf-8" && n != "windows-1252" { 116 b, err := c.NewDecoder().Bytes(respBody) 117 if err == nil { 118 return b 119 } 120 } 121 return respBody 122 } 123 124 func (r *Res) Body() (body io.ReadCloser) { 125 if r.err != nil { 126 return nil 127 } 128 if r.responseBody != nil { 129 return ioutil.NopCloser(bytes.NewReader(r.responseBody)) 130 } 131 defer r.resp.Body.Close() 132 respBody, err := ioutil.ReadAll(r.resp.Body) 133 _, _ = io.Copy(ioutil.Discard, r.resp.Body) 134 if err != nil { 135 r.err = err 136 return nil 137 } 138 139 r.responseBody = forceUTF8(r, respBody) 140 return ioutil.NopCloser(bytes.NewReader(r.responseBody)) 141 } 142 143 func (r *Res) HTML() (doc QueryHTML) { 144 data, err := r.ToBytes() 145 if err != nil { 146 return QueryHTML{} 147 } 148 doc, _ = HTMLParse(data) 149 return 150 } 151 152 func (r *Res) String() string { 153 data, _ := r.ToBytes() 154 return string(data) 155 } 156 157 func (r *Res) JSONs() *zjson.Res { 158 data, _ := r.ToBytes() 159 return zjson.ParseBytes(data) 160 } 161 162 func (r *Res) JSON(key string) *zjson.Res { 163 j := r.JSONs() 164 return j.Get(key) 165 } 166 167 func (r *Res) ToString() (string, error) { 168 data, err := r.ToBytes() 169 return string(data), err 170 } 171 172 func (r *Res) ToJSON(v interface{}) error { 173 data, err := r.ToBytes() 174 if err != nil { 175 return err 176 } 177 return json.Unmarshal(data, v) 178 } 179 180 func (r *Res) ToXML(v interface{}) error { 181 data, err := r.ToBytes() 182 if err != nil { 183 return err 184 } 185 return xml.Unmarshal(data, v) 186 } 187 188 func (r *Res) ToFile(name string) error { 189 nameSplit := strings.Split(zfile.RealPath(name), "/") 190 nameSplitLen := len(nameSplit) 191 if nameSplitLen > 1 { 192 dir := strings.Join(nameSplit[0:nameSplitLen-1], "/") 193 name = zfile.RealPathMkdir(dir) + "/" + nameSplit[nameSplitLen-1] 194 } 195 196 if r.tmpFile != "" { 197 return zfile.CopyFile(r.tmpFile, name) 198 } 199 200 file, err := os.Create(name) 201 if err != nil { 202 return err 203 } 204 //noinspection GoUnhandledErrorResult 205 defer file.Close() 206 207 if r.responseBody != nil { 208 _, err = file.Write(r.responseBody) 209 return err 210 } 211 212 if r.downloadProgress != nil && r.resp.ContentLength > 0 { 213 err = r.download(file) 214 } else { 215 //noinspection GoUnhandledErrorResult 216 defer r.resp.Body.Close() 217 _, err = io.Copy(file, r.resp.Body) 218 } 219 if err == nil { 220 r.tmpFile = name 221 } 222 return err 223 } 224 225 func (r *Res) download(file *os.File) error { 226 var ( 227 current int64 228 lastTime time.Time 229 ) 230 p, b := make([]byte, 1024), r.resp.Body 231 duration, total := 200*time.Millisecond, r.resp.ContentLength 232 //noinspection GoUnhandledErrorResult 233 defer b.Close() 234 for { 235 l, err := b.Read(p) 236 if l > 0 { 237 _, _err := file.Write(p[:l]) 238 if _err != nil { 239 return _err 240 } 241 current += int64(l) 242 if now := time.Now(); now.Sub(lastTime) > duration { 243 lastTime = now 244 r.downloadProgress(current, total) 245 } 246 } 247 if err != nil { 248 if err == io.EOF { 249 r.downloadProgress(total, total) 250 return nil 251 } 252 return err 253 } 254 } 255 }