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

相关推荐
pumpkin845146 小时前
01 Coze Studio项目中所有关键术语的详细解释和代码示例
golang
极安代理6 小时前
HTTP代理是什么?作用与场景全面解析
网络·网络协议·http
未来之窗软件服务7 小时前
幽冥大陆(一百07)—门禁局域网http获取名单—东方仙盟练气期
网络·http·仙盟创梦ide·东方仙盟·东方仙盟智能硬件·智能闸机
ps酷教程9 小时前
netty模拟文件列表http服务器
http·netty
AC赳赳老秦11 小时前
Dify工作流+DeepSeek:运维自动化闭环(数据采集→报告生成)
android·大数据·运维·数据库·人工智能·golang·deepseek
源代码•宸12 小时前
Leetcode—3. 无重复字符的最长子串【中等】
经验分享·后端·算法·leetcode·面试·golang·string
阳明Coding12 小时前
golang从入门到通天—数据库操作(gorm框架使用)(最简单最详细的golang学习笔记)
笔记·学习·golang
人道领域13 小时前
JavaWeb从入门到进阶(HTTP协议的请求与响应)
网络·网络协议·http
极安代理14 小时前
HTTP代理IP如何提升爬虫采集效率?
爬虫·tcp/ip·http
草根站起来15 小时前
https加密证书
网络协议·http·https