Go Http请求海康摄像头拍照图片

Go 复制代码
package main

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"

	randomString "github.com/delphinus/random-string"
	"golang.org/x/net/context"
)

type httpClientKey struct{}

var HTTPClientKey httpClientKey

func ContextWithClient(parent context.Context, client *http.Client) context.Context {
	return context.WithValue(parent, HTTPClientKey, client)
}

func clientFromContext(ctx context.Context) *http.Client {
	if client, ok := ctx.Value(HTTPClientKey).(*http.Client); ok {
		return client
	}
	return http.DefaultClient
}

type DigestRequest struct {
	context.Context
	client             *http.Client
	username, password string
	nonceCount         nonceCount
}

type nonceCount int

func (nc nonceCount) String() string {
	c := int(nc)
	return fmt.Sprintf("%08x", c)
}

const authorization = "Authorization"
const contentType = "Content-Type"
const nonce = "nonce"
const opaque = "opaque"
const qop = "qop"
const realm = "realm"
const wwwAuthenticate = "Www-Authenticate"

var wanted = []string{nonce, qop, realm}

func NewDigestRequest(ctx context.Context, username, password string) *DigestRequest {
	return &DigestRequest{
		Context:  ctx,
		client:   clientFromContext(ctx),
		username: username,
		password: password,
	}
}

func (r *DigestRequest) Do(req *http.Request) (*http.Response, error) {
	parts, err := r.makeParts(req)
	if err != nil {
		return nil, err
	}

	if parts != nil {
		aut := r.makeAuthorization(req, parts)
		fmt.Println("header:", aut)
		req.Header.Set(authorization, aut)
	}

	return r.client.Do(req)
}

func (r *DigestRequest) makeParts(req *http.Request) (map[string]string, error) {
	authReq, _ := http.NewRequest(req.Method, req.URL.String(), nil)
	resp, err := r.client.Do(authReq)
	if err != nil {
		return nil, err
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.StatusCode != http.StatusUnauthorized {
		return nil, nil
	}

	for _, vv := range resp.Header {
		fmt.Println(vv)
	}

	if len(resp.Header[wwwAuthenticate]) == 0 {
		return nil, fmt.Errorf("headers do not have %s", wwwAuthenticate)
	}

	headers := strings.Split(resp.Header[wwwAuthenticate][0], ",")
	parts := make(map[string]string, len(wanted))
	for _, r := range headers {
		for _, w := range wanted {
			if strings.Contains(r, w) {
				parts[w] = strings.Split(r, `"`)[1]
			}
		}
	}

	if len(parts) != len(wanted) {
		return nil, fmt.Errorf("header is invalid: %+v", parts)
	}

	return parts, nil
}

func getMD5(texts []string) string {
	h := md5.New()
	_, _ = io.WriteString(h, strings.Join(texts, ":"))
	return hex.EncodeToString(h.Sum(nil))
}

func (r *DigestRequest) getNonceCount() string {
	r.nonceCount++
	return r.nonceCount.String()
}

func (r *DigestRequest) makeAuthorization(req *http.Request, parts map[string]string) string {
	uri := req.URL.RequestURI()
	ha1 := getMD5([]string{r.username, parts[realm], r.password})
	ha2 := getMD5([]string{req.Method, uri})
	cnonce := randomString.Generate(16)
	nc := r.getNonceCount()
	response := getMD5([]string{
		ha1,
		parts[nonce],
		nc,
		cnonce,
		parts[qop],
		ha2,
	})
	return fmt.Sprintf(
		`Digest username="%s", realm="%s", nonce="%s", uri="%s", qop=%s, nc=%s, cnonce="%s", response="%s"`,
		r.username,
		parts[realm],
		parts[nonce],
		uri,
		parts[qop],
		nc,
		cnonce,
		response,
	)
}

func main() {
	url := "http://172.31.2.164:80/ISAPI/Streaming/channels/33/picture"
	username := "admin"
	password := "root123456"

	ctx := context.Background()
	r := NewDigestRequest(ctx, username, password) // username & password
	if r == nil {
		fmt.Println("unk")
		return
	}
	req, _ := http.NewRequest("GET", url, nil)
	resp, err := r.Do(req)

	if err != nil {
		fmt.Println("unk1", err)
		return
	}
	fmt.Println("Code:", resp.StatusCode)

	// 保存图片
	file, err := os.Create("1.jpeg")
	if err != nil {
		fmt.Println("Error creating file:", err)
		return
	}
	defer file.Close()

	_, err = io.Copy(file, resp.Body)
	if err != nil {
		fmt.Println("Error saving file:", err)
		return
	}

	fmt.Println("图片保存成功。")
}

这段代码实现了一个带有 Digest 认证的 HTTP 客户端,用于从指定 URL 获取图片并保存到本地。代码通过上下文传递 HTTP 客户端,并在需要时从上下文中提取客户端。代码还处理了 Digest 认证的具体细节,包括生成认证头和管理 nonce 计数。

相关推荐
st紫月27 分钟前
用vue和go实现登录加密
前端·vue.js·golang
YGGP2 小时前
浅析 Golang 内存管理
golang·内存泄露·内存逃逸
小白自救计划2 小时前
网络协议分析 实验七 FTP、HTTP、DHCP
网络·网络协议·http
Chandler242 小时前
Go 语言 net/http 包使用:HTTP 服务器、客户端与中间件
服务器·http·golang
z人间防沉迷k3 小时前
互联网协议的多路复用、Linux系统的I/O模式
linux·网络·http
z人间防沉迷k4 小时前
UDP和TCP协议
网络协议·tcp/ip·http·udp
Chandler244 小时前
Go语言:json 作用和语法
开发语言·golang·json
roman_日积跬步-终至千里4 小时前
【starrocks】StarRocks 常见 HTTP 操作与导入错误排查指南
starrocks·网络协议·http
Excuse_lighttime7 小时前
HTTP / HTTPS 协议
网络·网络协议·http·https
z人间防沉迷k7 小时前
TCP核心机制
网络·网络协议·tcp/ip·http