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

相关推荐
weixin_7275356220 小时前
HTTP 八股文:从三次握手到浏览器渲染的硬核拆解
网络·网络协议·http
久久学姐1 天前
Python开发爬虫的常用技术架构
爬虫·python·http·框架·数据存储
摇曳的精灵1 天前
HTTP 与 MCP:不是替代,而是分层
网络·网络协议·http·mcp
Cx330❀1 天前
【Linux网络】深入 HTTP 协议(五):从 Cookie/Session 原理到 C++ 源码实战
linux·运维·服务器·开发语言·网络·c++·http
北冥you鱼1 天前
Go语言四则运算实战:从基础类型到big包的深度解析
开发语言·后端·golang
Wang's Blog1 天前
Go-Zero 项目开发47:自研微服务框架的必要性与核心结构设计
开发语言·微服务·golang
名字还没想好☜2 天前
Go 用 bufio.Scanner 读大文件踩坑:默认 64KB 行上限、Buffer 扩容与按 Token 切分
开发语言·后端·golang·go
techdashen2 天前
Go设计取舍之六: sync.Mutex正常模式与饥饿模式
开发语言·后端·golang
组合缺一2 天前
Solon 的 10 种 HTTP 服务器:改一行依赖,换一个引擎
java·服务器·网络协议·http·solon
AI人工智能+电脑小能手2 天前
【大白话说Java面试题 第211题】【10_网络协议篇】第2题:说一下什么是 HTTP 协议?
java·网络协议·http·https·web通信