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 计数。

相关推荐
大、男人1 小时前
目标URL存在http host头攻击漏洞
网络·网络协议·http
月屯3 小时前
后端go完成文档分享链接功能
开发语言·后端·golang
喵了几个咪6 小时前
Golang微服务框架Kratos实现Thrift服务
开发语言·微服务·golang
Caarlossss6 小时前
jdbc学习
java·开发语言·学习·http·tomcat·maven
桃花岛主708 小时前
multipart/form-data 和 application/x-www-form-urlencoded区别
服务器·网络·网络协议·http
Zzz 小生9 小时前
Github-Go语言AI智能体开发套件:构建下一代智能代理的利器
人工智能·golang·github
m0_4955627810 小时前
https的原理
网络协议·http·https
TO_ZRG11 小时前
Base64原理、Http Header
网络·网络协议·http
q***829111 小时前
Nginx中$http_host、$host、$proxy_host的区别
运维·nginx·http
@hdd11 小时前
libsoup 调用http API报错 unacceptable TLS certificate
http·soup