add download method
This commit is contained in:
		
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | .idea/ | ||||||
|  | */.DS_Store | ||||||
|  | .vscode | ||||||
|  | go.sum | ||||||
							
								
								
									
										15
									
								
								client.go
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								client.go
									
									
									
									
									
								
							| @@ -155,42 +155,57 @@ func (c *Client) Use(middlewares ...MiddlewareFunc) *Client { | |||||||
| 	return c | 	return c | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Download download a file from the network address to the local. | ||||||
|  | func (c *Client) Download(url, dir string, filename ...string) (string, error) { | ||||||
|  | 	return NewDownload(c).Download(url, dir, filename...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Request send an http request. | ||||||
| func (c *Client) Request(method, url string, data ...interface{}) (*Response, error) { | func (c *Client) Request(method, url string, data ...interface{}) (*Response, error) { | ||||||
| 	return NewRequest(c).request(method, url, data...) | 	return NewRequest(c).request(method, url, data...) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Get send an http request use get method. | ||||||
| func (c *Client) Get(url string, data ...interface{}) (*Response, error) { | func (c *Client) Get(url string, data ...interface{}) (*Response, error) { | ||||||
| 	return c.Request(MethodGet, url, data...) | 	return c.Request(MethodGet, url, data...) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Post send an http request use post method. | ||||||
| func (c *Client) Post(url string, data ...interface{}) (*Response, error) { | func (c *Client) Post(url string, data ...interface{}) (*Response, error) { | ||||||
| 	return c.Request(MethodPost, url, data...) | 	return c.Request(MethodPost, url, data...) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Put send an http request use put method. | ||||||
| func (c *Client) Put(url string, data ...interface{}) (*Response, error) { | func (c *Client) Put(url string, data ...interface{}) (*Response, error) { | ||||||
| 	return c.Request(MethodPut, url, data...) | 	return c.Request(MethodPut, url, data...) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Patch send an http request use patch method. | ||||||
| func (c *Client) Patch(url string, data ...interface{}) (*Response, error) { | func (c *Client) Patch(url string, data ...interface{}) (*Response, error) { | ||||||
| 	return c.Request(MethodPatch, url, data...) | 	return c.Request(MethodPatch, url, data...) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Delete send an http request use patch method. | ||||||
| func (c *Client) Delete(url string, data ...interface{}) (*Response, error) { | func (c *Client) Delete(url string, data ...interface{}) (*Response, error) { | ||||||
| 	return c.Request(MethodDelete, url, data...) | 	return c.Request(MethodDelete, url, data...) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Head send an http request use head method. | ||||||
| func (c *Client) Head(url string, data ...interface{}) (*Response, error) { | func (c *Client) Head(url string, data ...interface{}) (*Response, error) { | ||||||
| 	return c.Request(MethodHead, url, data...) | 	return c.Request(MethodHead, url, data...) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Options send an http request use options method. | ||||||
| func (c *Client) Options(url string, data ...interface{}) (*Response, error) { | func (c *Client) Options(url string, data ...interface{}) (*Response, error) { | ||||||
| 	return c.Request(MethodOptions, url, data...) | 	return c.Request(MethodOptions, url, data...) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Connect send an http request use connect method. | ||||||
| func (c *Client) Connect(url string, data ...interface{}) (*Response, error) { | func (c *Client) Connect(url string, data ...interface{}) (*Response, error) { | ||||||
| 	return c.Request(MethodConnect, url, data...) | 	return c.Request(MethodConnect, url, data...) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Trace send an http request use trace method. | ||||||
| func (c *Client) Trace(url string, data ...interface{}) (*Response, error) { | func (c *Client) Trace(url string, data ...interface{}) (*Response, error) { | ||||||
| 	return c.Request(MethodTrace, url, data...) | 	return c.Request(MethodTrace, url, data...) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -59,23 +59,26 @@ func TestClient_Post(t *testing.T) { | |||||||
| 		Sort: 0, | 		Sort: 0, | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	//data := map[string]interface{}{ |  | ||||||
| 	//	"id":   1, |  | ||||||
| 	//	"pid":  0, |  | ||||||
| 	//	"code": "110000", |  | ||||||
| 	//	"name": "北京市", |  | ||||||
| 	//	"sort": 0, |  | ||||||
| 	//} |  | ||||||
| 	 |  | ||||||
| 	if resp, err := client.Put("/backend/region/update-region", data); err != nil { | 	if resp, err := client.Put("/backend/region/update-region", data); err != nil { | ||||||
| 		t.Error(err) | 		t.Error(err) | ||||||
| 		return | 		return | ||||||
| 	} else { | 	} else { | ||||||
| 		t.Log(resp.Response.Status) | 		t.Log(resp.Response.Status) | ||||||
| 		t.Log(resp.Response.Header) | 		t.Log(resp.Response.Header) | ||||||
| 		t.Log(resp.Bytes()) | 		t.Log(resp.ReadBytes()) | ||||||
| 		t.Log(resp.String()) | 		t.Log(resp.ReadString()) | ||||||
| 		t.Log(resp.GetHeaders()) | 		t.Log(resp.GetHeaders()) | ||||||
| 		t.Log(resp.GetCookies()) | 		t.Log(resp.GetCookies()) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestClient_Download(t *testing.T) { | ||||||
|  | 	url := "https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png" | ||||||
|  | 	 | ||||||
|  | 	if path, err := http.NewClient().Download(url, "./"); err != nil { | ||||||
|  | 		t.Error(err) | ||||||
|  | 		return | ||||||
|  | 	} else { | ||||||
|  | 		t.Log(path) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										87
									
								
								download.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								download.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | |||||||
|  | /** | ||||||
|  |  * @Author: fuxiao | ||||||
|  |  * @Email: 576101059@qq.com | ||||||
|  |  * @Date: 2021/8/26 1:59 下午 | ||||||
|  |  * @Desc: TODO | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package http | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"os" | ||||||
|  | 	"strings" | ||||||
|  | 	 | ||||||
|  | 	"github.com/dobyte/http/internal" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var contentTypeToFileSuffix = map[string]string{ | ||||||
|  | 	"application/x-001":              ".001", | ||||||
|  | 	"text/h323":                      ".323", | ||||||
|  | 	"drawing/907":                    ".907", | ||||||
|  | 	"audio/x-mei-aac":                ".acp", | ||||||
|  | 	"audio/aiff":                     ".aif", | ||||||
|  | 	"text/asa":                       ".asa", | ||||||
|  | 	"text/asp":                       ".asp", | ||||||
|  | 	"audio/basic":                    ".au", | ||||||
|  | 	"application/vnd.adobe.workflow": ".awf", | ||||||
|  | 	"application/x-bmp":              ".bmp", | ||||||
|  | 	"application/x-c4t":              ".c4t", | ||||||
|  | 	"application/x-cals":             ".cal", | ||||||
|  | 	"application/x-netcdf":           ".cdf", | ||||||
|  | 	"application/x-cel":              ".cel", | ||||||
|  | 	"application/x-g4":               ".cg4", | ||||||
|  | 	"application/x-cit":              ".cit", | ||||||
|  | 	"text/xml":                       ".cml", | ||||||
|  | 	"application/x-cmx":              ".cmx", | ||||||
|  | 	"application/pkix-crl":           ".crl", | ||||||
|  | 	"application/x-csi":              ".csi", | ||||||
|  | 	"application/x-cut":              ".cut", | ||||||
|  | 	"application/x-dbm":              ".dbm", | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Download struct { | ||||||
|  | 	request *Request | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewDownload(c *Client) *Download { | ||||||
|  | 	return &Download{ | ||||||
|  | 		request: NewRequest(c), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Download download a file from the network address to the local. | ||||||
|  | func (d *Download) Download(url, dir string, filename ...string) (string, error) { | ||||||
|  | 	resp, err := d.request.request(MethodGet, url) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	var path string | ||||||
|  | 	 | ||||||
|  | 	if len(filename) > 0 { | ||||||
|  | 		path = strings.TrimRight(dir, string(os.PathSeparator)) + string(os.PathSeparator) + filename[0] | ||||||
|  | 	} else { | ||||||
|  | 		path = d.genFilePath(resp, dir) | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	if err = internal.SaveToFile(path, resp.ReadBytes()); err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	return path, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // genFilePath generate file path based on response content type | ||||||
|  | func (d *Download) genFilePath(resp *Response, dir string) string { | ||||||
|  | 	path := strings.TrimRight(dir, string(os.PathSeparator)) + string(os.PathSeparator) + internal.RandStr(16) | ||||||
|  | 	 | ||||||
|  | 	if suffix := internal.GetFileType(resp.ReadBytes()); suffix != "" { | ||||||
|  | 		path += "." + suffix | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	if internal.Exists(path) { | ||||||
|  | 		return d.genFilePath(resp, dir) | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	return path | ||||||
|  | } | ||||||
							
								
								
									
										234
									
								
								internal/conv.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								internal/conv.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,234 @@ | |||||||
|  | /** | ||||||
|  |  * @Author: fuxiao | ||||||
|  |  * @Email: 576101059@qq.com | ||||||
|  |  * @Date: 2021/8/26 5:00 下午 | ||||||
|  |  * @Desc: TODO | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package internal | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding" | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"reflect" | ||||||
|  | 	"strconv" | ||||||
|  | 	"time" | ||||||
|  | 	"unsafe" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type stringInterface interface { | ||||||
|  | 	String() string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type errorInterface interface { | ||||||
|  | 	Error() string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func String(any interface{}) string { | ||||||
|  | 	switch v := any.(type) { | ||||||
|  | 	case nil: | ||||||
|  | 		return "" | ||||||
|  | 	case string: | ||||||
|  | 		return v | ||||||
|  | 	case int: | ||||||
|  | 		return strconv.Itoa(v) | ||||||
|  | 	case int8: | ||||||
|  | 		return strconv.Itoa(int(v)) | ||||||
|  | 	case int16: | ||||||
|  | 		return strconv.Itoa(int(v)) | ||||||
|  | 	case int32: | ||||||
|  | 		return strconv.Itoa(int(v)) | ||||||
|  | 	case int64: | ||||||
|  | 		return strconv.FormatInt(v, 10) | ||||||
|  | 	case uint: | ||||||
|  | 		return strconv.FormatUint(uint64(v), 10) | ||||||
|  | 	case uint8: | ||||||
|  | 		return strconv.FormatUint(uint64(v), 10) | ||||||
|  | 	case uint16: | ||||||
|  | 		return strconv.FormatUint(uint64(v), 10) | ||||||
|  | 	case uint64: | ||||||
|  | 		return strconv.FormatUint(v, 10) | ||||||
|  | 	case float32: | ||||||
|  | 		return strconv.FormatFloat(float64(v), 'f', -1, 32) | ||||||
|  | 	case float64: | ||||||
|  | 		return strconv.FormatFloat(v, 'f', -1, 64) | ||||||
|  | 	case bool: | ||||||
|  | 		return strconv.FormatBool(v) | ||||||
|  | 	case []byte: | ||||||
|  | 		return string(v) | ||||||
|  | 	case time.Time: | ||||||
|  | 		return v.String() | ||||||
|  | 	case *time.Time: | ||||||
|  | 		if v == nil { | ||||||
|  | 			return "" | ||||||
|  | 		} | ||||||
|  | 		return v.String() | ||||||
|  | 	default: | ||||||
|  | 		if v == nil { | ||||||
|  | 			return "" | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		if i, ok := v.(stringInterface); ok { | ||||||
|  | 			return i.String() | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		if i, ok := v.(errorInterface); ok { | ||||||
|  | 			return i.Error() | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		var ( | ||||||
|  | 			rv   = reflect.ValueOf(v) | ||||||
|  | 			kind = rv.Kind() | ||||||
|  | 		) | ||||||
|  | 		 | ||||||
|  | 		switch kind { | ||||||
|  | 		case reflect.Chan, | ||||||
|  | 			reflect.Map, | ||||||
|  | 			reflect.Slice, | ||||||
|  | 			reflect.Func, | ||||||
|  | 			reflect.Ptr, | ||||||
|  | 			reflect.Interface, | ||||||
|  | 			reflect.UnsafePointer: | ||||||
|  | 			if rv.IsNil() { | ||||||
|  | 				return "" | ||||||
|  | 			} | ||||||
|  | 		case reflect.String: | ||||||
|  | 			return rv.String() | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		if kind == reflect.Ptr { | ||||||
|  | 			return String(rv.Elem().Interface()) | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		if b, e := json.Marshal(v); e != nil { | ||||||
|  | 			return fmt.Sprint(v) | ||||||
|  | 		} else { | ||||||
|  | 			return string(b) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Scan(b []byte, any interface{}) error { | ||||||
|  | 	switch v := any.(type) { | ||||||
|  | 	case nil: | ||||||
|  | 		return fmt.Errorf("cache: Scan(nil)") | ||||||
|  | 	case *string: | ||||||
|  | 		*v = String(b) | ||||||
|  | 		return nil | ||||||
|  | 	case *[]byte: | ||||||
|  | 		*v = b | ||||||
|  | 		return nil | ||||||
|  | 	case *int: | ||||||
|  | 		var err error | ||||||
|  | 		*v, err = strconv.Atoi(String(b)) | ||||||
|  | 		return err | ||||||
|  | 	case *int8: | ||||||
|  | 		n, err := strconv.ParseInt(String(b), 10, 8) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		*v = int8(n) | ||||||
|  | 		return nil | ||||||
|  | 	case *int16: | ||||||
|  | 		n, err := strconv.ParseInt(String(b), 10, 16) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		*v = int16(n) | ||||||
|  | 		return nil | ||||||
|  | 	case *int32: | ||||||
|  | 		n, err := strconv.ParseInt(String(b), 10, 32) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		*v = int32(n) | ||||||
|  | 		return nil | ||||||
|  | 	case *int64: | ||||||
|  | 		n, err := strconv.ParseInt(String(b), 10, 64) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		*v = n | ||||||
|  | 		return nil | ||||||
|  | 	case *uint: | ||||||
|  | 		n, err := strconv.ParseUint(String(b), 10, 64) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		*v = uint(n) | ||||||
|  | 		return nil | ||||||
|  | 	case *uint8: | ||||||
|  | 		n, err := strconv.ParseUint(String(b), 10, 8) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		*v = uint8(n) | ||||||
|  | 		return nil | ||||||
|  | 	case *uint16: | ||||||
|  | 		n, err := strconv.ParseUint(String(b), 10, 16) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		*v = uint16(n) | ||||||
|  | 		return nil | ||||||
|  | 	case *uint32: | ||||||
|  | 		n, err := strconv.ParseUint(String(b), 10, 32) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		*v = uint32(n) | ||||||
|  | 		return nil | ||||||
|  | 	case *uint64: | ||||||
|  | 		n, err := strconv.ParseUint(String(b), 10, 64) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		*v = n | ||||||
|  | 		return nil | ||||||
|  | 	case *float32: | ||||||
|  | 		n, err := strconv.ParseFloat(String(b), 32) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		*v = float32(n) | ||||||
|  | 		return err | ||||||
|  | 	case *float64: | ||||||
|  | 		var err error | ||||||
|  | 		*v, err = strconv.ParseFloat(String(b), 64) | ||||||
|  | 		return err | ||||||
|  | 	case *bool: | ||||||
|  | 		*v = len(b) == 1 && b[0] == '1' | ||||||
|  | 		return nil | ||||||
|  | 	case *time.Time: | ||||||
|  | 		var err error | ||||||
|  | 		*v, err = time.Parse(time.RFC3339Nano, String(b)) | ||||||
|  | 		return err | ||||||
|  | 	case encoding.BinaryUnmarshaler: | ||||||
|  | 		return v.UnmarshalBinary(b) | ||||||
|  | 	default: | ||||||
|  | 		var ( | ||||||
|  | 			rv   = reflect.ValueOf(v) | ||||||
|  | 			kind = rv.Kind() | ||||||
|  | 		) | ||||||
|  | 		 | ||||||
|  | 		if kind != reflect.Ptr { | ||||||
|  | 			return fmt.Errorf("can't unmarshal %T", v) | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		switch kind = rv.Elem().Kind(); kind { | ||||||
|  | 		case reflect.Array, reflect.Slice, reflect.Map, reflect.Struct: | ||||||
|  | 			return json.Unmarshal(b, v) | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		return fmt.Errorf("can't unmarshal %T", v) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func UnsafeStringToBytes(s string) []byte { | ||||||
|  | 	return *(*[]byte)(unsafe.Pointer(&s)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func UnsafeBytesToString(b []byte) string { | ||||||
|  | 	return *(*string)(unsafe.Pointer(&b)) | ||||||
|  | } | ||||||
							
								
								
									
										66
									
								
								internal/file.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								internal/file.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | |||||||
|  | /** | ||||||
|  |  * @Author: fuxiao | ||||||
|  |  * @Email: 576101059@qq.com | ||||||
|  |  * @Date: 2021/8/26 4:59 下午 | ||||||
|  |  * @Desc: TODO | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package internal | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"io" | ||||||
|  | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Exists check if the file or path exists. | ||||||
|  | func Exists(path string) bool { | ||||||
|  | 	if stat, err := os.Stat(path); stat != nil && !os.IsNotExist(err) { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SaveToFile save data to file. | ||||||
|  | func SaveToFile(path string, data []byte) error { | ||||||
|  | 	dir := filepath.Dir(path) | ||||||
|  | 	if !Exists(dir) { | ||||||
|  | 		if err := MakeDir(dir); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(0666)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer f.Close() | ||||||
|  | 	 | ||||||
|  | 	if n, err := f.Write(data); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} else if n < len(data) { | ||||||
|  | 		return io.ErrShortWrite | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MakeDir create directories recursively. | ||||||
|  | func MakeDir(dir string) error { | ||||||
|  | 	if err := os.MkdirAll(dir, os.ModePerm); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RealPath get the real path. | ||||||
|  | func RealPath(path string) string { | ||||||
|  | 	p, err := filepath.Abs(path) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  | 	if !Exists(p) { | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  | 	return p | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								internal/rand.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								internal/rand.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | /** | ||||||
|  |  * @Author: fuxiao | ||||||
|  |  * @Email: 576101059@qq.com | ||||||
|  |  * @Date: 2021/8/26 4:51 下午 | ||||||
|  |  * @Desc: TODO | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package internal | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"math/rand" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var seedStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" | ||||||
|  |  | ||||||
|  | // RandStr generate a string of specified length. | ||||||
|  | func RandStr(length int) (lastStr string) { | ||||||
|  | 	rand.Seed(time.Now().UnixNano()) | ||||||
|  | 	 | ||||||
|  | 	pos, seedLen := 0, len(seedStr) | ||||||
|  | 	for i := 0; i < length; i++ { | ||||||
|  | 		pos = rand.Intn(seedLen) | ||||||
|  | 		lastStr += seedStr[pos : pos+1] | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	return lastStr | ||||||
|  | } | ||||||
							
								
								
									
										120
									
								
								internal/stream.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								internal/stream.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | |||||||
|  | /** | ||||||
|  |  * @Author: fuxiao | ||||||
|  |  * @Email: 576101059@qq.com | ||||||
|  |  * @Date: 2021/8/26 6:21 下午 | ||||||
|  |  * @Desc: TODO | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | package internal | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"encoding/hex" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  | 	"sync" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var fileTypeMap sync.Map | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	fileTypeMap.Store("ffd8ffe000104a464946", "jpg") | ||||||
|  | 	fileTypeMap.Store("89504e470d0a1a0a0000", "png") | ||||||
|  | 	fileTypeMap.Store("47494638396126026f01", "gif") | ||||||
|  | 	fileTypeMap.Store("49492a00227105008037", "tif") | ||||||
|  | 	fileTypeMap.Store("424d228c010000000000", "bmp") | ||||||
|  | 	fileTypeMap.Store("424d8240090000000000", "bmp") | ||||||
|  | 	fileTypeMap.Store("424d8e1b030000000000", "bmp") | ||||||
|  | 	fileTypeMap.Store("41433130313500000000", "dwg") | ||||||
|  | 	fileTypeMap.Store("3c21444f435459504520", "html") | ||||||
|  | 	fileTypeMap.Store("3c68746d6c3e0", "html") | ||||||
|  | 	fileTypeMap.Store("3c21646f637479706520", "htm") | ||||||
|  | 	fileTypeMap.Store("48544d4c207b0d0a0942", "css") | ||||||
|  | 	fileTypeMap.Store("696b2e71623d696b2e71", "js") | ||||||
|  | 	fileTypeMap.Store("7b5c727466315c616e73", "rtf") | ||||||
|  | 	fileTypeMap.Store("38425053000100000000", "psd") | ||||||
|  | 	fileTypeMap.Store("46726f6d3a203d3f6762", "eml") | ||||||
|  | 	fileTypeMap.Store("d0cf11e0a1b11ae10000", "doc") | ||||||
|  | 	fileTypeMap.Store("d0cf11e0a1b11ae10000", "vsd") | ||||||
|  | 	fileTypeMap.Store("5374616E64617264204A", "mdb") | ||||||
|  | 	fileTypeMap.Store("252150532D41646F6265", "ps") | ||||||
|  | 	fileTypeMap.Store("255044462d312e350d0a", "pdf") | ||||||
|  | 	fileTypeMap.Store("2e524d46000000120001", "rmvb") | ||||||
|  | 	fileTypeMap.Store("464c5601050000000900", "flv") | ||||||
|  | 	fileTypeMap.Store("00000020667479706d70", "mp4") | ||||||
|  | 	fileTypeMap.Store("49443303000000002176", "mp3") | ||||||
|  | 	fileTypeMap.Store("000001ba210001000180", "mpg") | ||||||
|  | 	fileTypeMap.Store("3026b2758e66cf11a6d9", "wmv") | ||||||
|  | 	fileTypeMap.Store("52494646e27807005741", "wav") | ||||||
|  | 	fileTypeMap.Store("52494646d07d60074156", "avi") | ||||||
|  | 	fileTypeMap.Store("4d546864000000060001", "mid") | ||||||
|  | 	fileTypeMap.Store("504b0304140000000800", "zip") | ||||||
|  | 	fileTypeMap.Store("526172211a0700cf9073", "rar") | ||||||
|  | 	fileTypeMap.Store("235468697320636f6e66", "ini") | ||||||
|  | 	fileTypeMap.Store("504b03040a0000000000", "jar") | ||||||
|  | 	fileTypeMap.Store("4d5a9000030000000400", "exe") | ||||||
|  | 	fileTypeMap.Store("3c25402070616765206c", "jsp") | ||||||
|  | 	fileTypeMap.Store("4d616e69666573742d56", "mf") | ||||||
|  | 	fileTypeMap.Store("3c3f786d6c2076657273", "xml") | ||||||
|  | 	fileTypeMap.Store("494e5345525420494e54", "sql") | ||||||
|  | 	fileTypeMap.Store("7061636b616765207765", "java") | ||||||
|  | 	fileTypeMap.Store("406563686f206f66660d", "bat") | ||||||
|  | 	fileTypeMap.Store("1f8b0800000000000000", "gz") | ||||||
|  | 	fileTypeMap.Store("6c6f67346a2e726f6f74", "properties") | ||||||
|  | 	fileTypeMap.Store("cafebabe0000002e0041", "class") | ||||||
|  | 	fileTypeMap.Store("49545346030000006000", "chm") | ||||||
|  | 	fileTypeMap.Store("04000000010000001300", "mxp") | ||||||
|  | 	fileTypeMap.Store("504b0304140006000800", "docx") | ||||||
|  | 	fileTypeMap.Store("d0cf11e0a1b11ae10000", "wps") | ||||||
|  | 	fileTypeMap.Store("6431303a637265617465", "torrent") | ||||||
|  | 	fileTypeMap.Store("6D6F6F76", "mov") | ||||||
|  | 	fileTypeMap.Store("FF575043", "wpd") | ||||||
|  | 	fileTypeMap.Store("CFAD12FEC5FD746F", "dbx") | ||||||
|  | 	fileTypeMap.Store("2142444E", "pst") | ||||||
|  | 	fileTypeMap.Store("AC9EBD8F", "qdf") | ||||||
|  | 	fileTypeMap.Store("E3828596", "pwl") | ||||||
|  | 	fileTypeMap.Store("2E7261FD", "ram") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // bytesToHexString get the binary of the previous result byte. | ||||||
|  | func bytesToHexString(stream []byte) string { | ||||||
|  | 	if stream == nil || len(stream) <= 0 { | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	var ( | ||||||
|  | 		hv   string | ||||||
|  | 		res  = bytes.Buffer{} | ||||||
|  | 		temp = make([]byte, 0) | ||||||
|  | 	) | ||||||
|  | 	 | ||||||
|  | 	for _, v := range stream { | ||||||
|  | 		if hv = hex.EncodeToString(append(temp, v&0xFF)); len(hv) < 2 { | ||||||
|  | 			res.WriteString(strconv.FormatInt(int64(0), 10)) | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		res.WriteString(hv) | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	return res.String() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetFileType judge the file type based on the binary byte stream. | ||||||
|  | func GetFileType(stream []byte) string { | ||||||
|  | 	var ( | ||||||
|  | 		fileType string | ||||||
|  | 		fileCode = bytesToHexString(stream) | ||||||
|  | 	) | ||||||
|  | 	 | ||||||
|  | 	fileTypeMap.Range(func(key, value interface{}) bool { | ||||||
|  | 		if strings.HasPrefix(fileCode, strings.ToLower(key.(string))) || | ||||||
|  | 			strings.HasPrefix(key.(string), strings.ToLower(fileCode)) { | ||||||
|  | 			fileType = value.(string) | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		return true | ||||||
|  | 	}) | ||||||
|  | 	 | ||||||
|  | 	return fileType | ||||||
|  | } | ||||||
| @@ -8,27 +8,13 @@ | |||||||
| package internal | package internal | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"encoding" |  | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" |  | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"os" |  | ||||||
| 	"reflect" |  | ||||||
| 	"strconv" |  | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" |  | ||||||
| 	"unsafe" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const fileUploadingKey = "@file:" | const fileUploadingKey = "@file:" | ||||||
|  |  | ||||||
| func Exists(path string) bool { |  | ||||||
| 	if stat, err := os.Stat(path); stat != nil && !os.IsNotExist(err) { |  | ||||||
| 		return true |  | ||||||
| 	} |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func BuildParams(params interface{}) string { | func BuildParams(params interface{}) string { | ||||||
| 	switch v := params.(type) { | 	switch v := params.(type) { | ||||||
| 	case string: | 	case string: | ||||||
| @@ -86,219 +72,3 @@ func BuildParams(params interface{}) string { | |||||||
| 	 | 	 | ||||||
| 	return str | 	return str | ||||||
| } | } | ||||||
|  |  | ||||||
| func String(any interface{}) string { |  | ||||||
| 	switch v := any.(type) { |  | ||||||
| 	case nil: |  | ||||||
| 		return "" |  | ||||||
| 	case string: |  | ||||||
| 		return v |  | ||||||
| 	case int: |  | ||||||
| 		return strconv.Itoa(v) |  | ||||||
| 	case int8: |  | ||||||
| 		return strconv.Itoa(int(v)) |  | ||||||
| 	case int16: |  | ||||||
| 		return strconv.Itoa(int(v)) |  | ||||||
| 	case int32: |  | ||||||
| 		return strconv.Itoa(int(v)) |  | ||||||
| 	case int64: |  | ||||||
| 		return strconv.FormatInt(v, 10) |  | ||||||
| 	case uint: |  | ||||||
| 		return strconv.FormatUint(uint64(v), 10) |  | ||||||
| 	case uint8: |  | ||||||
| 		return strconv.FormatUint(uint64(v), 10) |  | ||||||
| 	case uint16: |  | ||||||
| 		return strconv.FormatUint(uint64(v), 10) |  | ||||||
| 	case uint64: |  | ||||||
| 		return strconv.FormatUint(v, 10) |  | ||||||
| 	case float32: |  | ||||||
| 		return strconv.FormatFloat(float64(v), 'f', -1, 32) |  | ||||||
| 	case float64: |  | ||||||
| 		return strconv.FormatFloat(v, 'f', -1, 64) |  | ||||||
| 	case bool: |  | ||||||
| 		return strconv.FormatBool(v) |  | ||||||
| 	case []byte: |  | ||||||
| 		return string(v) |  | ||||||
| 	case time.Time: |  | ||||||
| 		return v.String() |  | ||||||
| 	case *time.Time: |  | ||||||
| 		if v == nil { |  | ||||||
| 			return "" |  | ||||||
| 		} |  | ||||||
| 		return v.String() |  | ||||||
| 	default: |  | ||||||
| 		if v == nil { |  | ||||||
| 			return "" |  | ||||||
| 		} |  | ||||||
| 		 |  | ||||||
| 		if i, ok := v.(stringInterface); ok { |  | ||||||
| 			return i.String() |  | ||||||
| 		} |  | ||||||
| 		 |  | ||||||
| 		if i, ok := v.(errorInterface); ok { |  | ||||||
| 			return i.Error() |  | ||||||
| 		} |  | ||||||
| 		 |  | ||||||
| 		var ( |  | ||||||
| 			rv   = reflect.ValueOf(v) |  | ||||||
| 			kind = rv.Kind() |  | ||||||
| 		) |  | ||||||
| 		 |  | ||||||
| 		switch kind { |  | ||||||
| 		case reflect.Chan, |  | ||||||
| 			reflect.Map, |  | ||||||
| 			reflect.Slice, |  | ||||||
| 			reflect.Func, |  | ||||||
| 			reflect.Ptr, |  | ||||||
| 			reflect.Interface, |  | ||||||
| 			reflect.UnsafePointer: |  | ||||||
| 			if rv.IsNil() { |  | ||||||
| 				return "" |  | ||||||
| 			} |  | ||||||
| 		case reflect.String: |  | ||||||
| 			return rv.String() |  | ||||||
| 		} |  | ||||||
| 		 |  | ||||||
| 		if kind == reflect.Ptr { |  | ||||||
| 			return String(rv.Elem().Interface()) |  | ||||||
| 		} |  | ||||||
| 		 |  | ||||||
| 		if b, e := json.Marshal(v); e != nil { |  | ||||||
| 			return fmt.Sprint(v) |  | ||||||
| 		} else { |  | ||||||
| 			return string(b) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Scan(b []byte, any interface{}) error { |  | ||||||
| 	switch v := any.(type) { |  | ||||||
| 	case nil: |  | ||||||
| 		return fmt.Errorf("cache: Scan(nil)") |  | ||||||
| 	case *string: |  | ||||||
| 		*v = String(b) |  | ||||||
| 		return nil |  | ||||||
| 	case *[]byte: |  | ||||||
| 		*v = b |  | ||||||
| 		return nil |  | ||||||
| 	case *int: |  | ||||||
| 		var err error |  | ||||||
| 		*v, err = strconv.Atoi(String(b)) |  | ||||||
| 		return err |  | ||||||
| 	case *int8: |  | ||||||
| 		n, err := strconv.ParseInt(String(b), 10, 8) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		*v = int8(n) |  | ||||||
| 		return nil |  | ||||||
| 	case *int16: |  | ||||||
| 		n, err := strconv.ParseInt(String(b), 10, 16) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		*v = int16(n) |  | ||||||
| 		return nil |  | ||||||
| 	case *int32: |  | ||||||
| 		n, err := strconv.ParseInt(String(b), 10, 32) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		*v = int32(n) |  | ||||||
| 		return nil |  | ||||||
| 	case *int64: |  | ||||||
| 		n, err := strconv.ParseInt(String(b), 10, 64) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		*v = n |  | ||||||
| 		return nil |  | ||||||
| 	case *uint: |  | ||||||
| 		n, err := strconv.ParseUint(String(b), 10, 64) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		*v = uint(n) |  | ||||||
| 		return nil |  | ||||||
| 	case *uint8: |  | ||||||
| 		n, err := strconv.ParseUint(String(b), 10, 8) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		*v = uint8(n) |  | ||||||
| 		return nil |  | ||||||
| 	case *uint16: |  | ||||||
| 		n, err := strconv.ParseUint(String(b), 10, 16) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		*v = uint16(n) |  | ||||||
| 		return nil |  | ||||||
| 	case *uint32: |  | ||||||
| 		n, err := strconv.ParseUint(String(b), 10, 32) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		*v = uint32(n) |  | ||||||
| 		return nil |  | ||||||
| 	case *uint64: |  | ||||||
| 		n, err := strconv.ParseUint(String(b), 10, 64) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		*v = n |  | ||||||
| 		return nil |  | ||||||
| 	case *float32: |  | ||||||
| 		n, err := strconv.ParseFloat(String(b), 32) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		*v = float32(n) |  | ||||||
| 		return err |  | ||||||
| 	case *float64: |  | ||||||
| 		var err error |  | ||||||
| 		*v, err = strconv.ParseFloat(String(b), 64) |  | ||||||
| 		return err |  | ||||||
| 	case *bool: |  | ||||||
| 		*v = len(b) == 1 && b[0] == '1' |  | ||||||
| 		return nil |  | ||||||
| 	case *time.Time: |  | ||||||
| 		var err error |  | ||||||
| 		*v, err = time.Parse(time.RFC3339Nano, String(b)) |  | ||||||
| 		return err |  | ||||||
| 	case encoding.BinaryUnmarshaler: |  | ||||||
| 		return v.UnmarshalBinary(b) |  | ||||||
| 	default: |  | ||||||
| 		var ( |  | ||||||
| 			rv   = reflect.ValueOf(v) |  | ||||||
| 			kind = rv.Kind() |  | ||||||
| 		) |  | ||||||
| 		 |  | ||||||
| 		if kind != reflect.Ptr { |  | ||||||
| 			return fmt.Errorf("can't unmarshal %T", v) |  | ||||||
| 		} |  | ||||||
| 		 |  | ||||||
| 		switch kind = rv.Elem().Kind(); kind { |  | ||||||
| 		case reflect.Array, reflect.Slice, reflect.Map, reflect.Struct: |  | ||||||
| 			return json.Unmarshal(b, v) |  | ||||||
| 		} |  | ||||||
| 		 |  | ||||||
| 		return fmt.Errorf("can't unmarshal %T", v) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type stringInterface interface { |  | ||||||
| 	String() string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type errorInterface interface { |  | ||||||
| 	Error() string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func UnsafeStringToBytes(s string) []byte { |  | ||||||
| 	return *(*[]byte)(unsafe.Pointer(&s)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func UnsafeBytesToString(b []byte) string { |  | ||||||
| 	return *(*string)(unsafe.Pointer(&b)) |  | ||||||
| } |  | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								response.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								response.go
									
									
									
									
									
								
							| @@ -10,6 +10,7 @@ package http | |||||||
| import ( | import ( | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"sync" | ||||||
| 	 | 	 | ||||||
| 	"github.com/dobyte/http/internal" | 	"github.com/dobyte/http/internal" | ||||||
| ) | ) | ||||||
| @@ -19,16 +20,19 @@ type Response struct { | |||||||
| 	Request *http.Request | 	Request *http.Request | ||||||
| 	body    []byte | 	body    []byte | ||||||
| 	cookies map[string]string | 	cookies map[string]string | ||||||
|  | 	mu      sync.Mutex | ||||||
| } | } | ||||||
|  |  | ||||||
| // Bytes retrieves and returns the response content as []byte. | // ReadBytes retrieves and returns the response content as []byte. | ||||||
| func (r *Response) Bytes() []byte { | func (r *Response) ReadBytes() []byte { | ||||||
| 	if r == nil || r.Response == nil { | 	if r == nil || r.Response == nil { | ||||||
| 		return []byte{} | 		return []byte{} | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	if r.body == nil { | 	if r.body == nil { | ||||||
| 		var err error | 		var err error | ||||||
|  | 		r.mu.Lock() | ||||||
|  | 		defer r.mu.Unlock() | ||||||
| 		if r.body, err = ioutil.ReadAll(r.Response.Body); err != nil { | 		if r.body, err = ioutil.ReadAll(r.Response.Body); err != nil { | ||||||
| 			return nil | 			return nil | ||||||
| 		} | 		} | ||||||
| @@ -37,14 +41,14 @@ func (r *Response) Bytes() []byte { | |||||||
| 	return r.body | 	return r.body | ||||||
| } | } | ||||||
|  |  | ||||||
| // String retrieves and returns the response content as string. | // ReadString retrieves and returns the response content as string. | ||||||
| func (r *Response) String() string { | func (r *Response) ReadString() string { | ||||||
| 	return internal.UnsafeBytesToString(r.Bytes()) | 	return internal.UnsafeBytesToString(r.ReadBytes()) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Scan convert the response into a complex data structure. | // Scan convert the response into a complex data structure. | ||||||
| func (r *Response) Scan(any interface{}) error { | func (r *Response) Scan(any interface{}) error { | ||||||
| 	return internal.Scan(r.Bytes(), any) | 	return internal.Scan(r.ReadBytes(), any) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Close closes the response when it will never be used. | // Close closes the response when it will never be used. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user