DAHUA

大华监控设备实时快照获取

获取步骤

接口地址

http://【ip】/cgi-bin/snapshot.cgi:其中【ip】是监控平台的实际ip地址

参数

channel:频道id,类型数字或字符串都可

Digest Auth验证

需要进行Digest Auth验证,输入username和password

监控预览

1749537029820.jpg

Apifox接口调用

软件:foxapi

系统:win10

步骤:GET请求,输入api地址,添加Params参数channel=9,并进行【Auth】验证,请求结果如下:

image.png

1749537754687.jpg

Go-Gin框架

代码

const (
    DefaultUsername = "" // 默认用户名
    DefaultPassword = "" // 默认密码
    DefaultUrl      = "" // 默认地址
    UploadAPI       = "" // 快照上传接口地址
)
// 获取摄像头快照
func GetCameraSnapshot(baseurl string, channel string, username string, password string) ([]byte, error) {
	// 请求地址
	if baseurl == "" {
		baseurl = DefaultUrl
	}
	// 用户名
	if username == "" {
		username = DefaultUsername
	}
	// 密码
	if password == "" {
		password = DefaultPassword
	}

	url := fmt.Sprintf("%s/cgi-bin/snapshot.cgi?channel=%s", baseurl, channel)

	// 使用 DigestAuth 发送请求
	digestAuth := NewDigestAuth(username, password)
	return digestAuth.Request("GET", url, nil, nil)
}
// 上传图片到远程服务器
func UploadImage(imageData []byte) (string, error) {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	// 照片上传文件夹【DaHua】
	_ = writer.WriteField("fileName", "DaHua")

	// 创建文件字段,并手动设置 Content-Type 为 image/jpeg
	_, err := writer.CreateFormFile("file", "dahua.jpg")
	if err != nil {
		return "", fmt.Errorf("failed to create form file: %v", err)
	}

	// 创建一个多部分写入器,并设置 Content-Type
	partHeader := make(textproto.MIMEHeader)
	partHeader.Set("Content-Disposition", `form-data; name="file"; filename="dahua.jpg"`)
	partHeader.Set("Content-Type", "image/jpeg") // 关键修改:明确设置 MIME 类型

	filePart, err := writer.CreatePart(partHeader)
	if err != nil {
		return "", fmt.Errorf("failed to create part: %v", err)
	}

	// 写入图像数据
	_, err = io.Copy(filePart, bytes.NewReader(imageData))
	if err != nil {
		return "", fmt.Errorf("failed to write file data: %v", err)
	}

	// 关闭 multipart writer
	err = writer.Close()
	if err != nil {
		return "", fmt.Errorf("failed to close multipart writer: %v", err)
	}

	// 发送 POST 请求
	req, err := http.NewRequest("POST", UploadAPI, body)
	if err != nil {
		return "", fmt.Errorf("failed to create request: %v", err)
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("failed to send upload request: %v", err)
	}
	defer resp.Body.Close()

	// 解析返回的 JSON
	var uploadResp UploadResponse
	err = json.NewDecoder(resp.Body).Decode(&uploadResp)
	if err != nil {
		return "", fmt.Errorf("failed to decode response: %v", err)
	}

	// 检查上传是否成功
	if uploadResp.Ret != 200 || uploadResp.Data.Code != 1 {
		return "", fmt.Errorf("upload failed: %s", uploadResp.Msg)
	}

	return uploadResp.Data.URL, nil
}
// 直接获取照片
DaHuaMonitor.GET("/Test", func(c *gin.Context) {
			channel := c.DefaultQuery("channel", "9")
			url := c.Query("url")
			username := c.Query("username")
			password := c.Query("password")

			// 获取摄像头照片
			imageData, err := GetCameraSnapshot(url, channel, username, password)
			if err != nil {
				ReturnError(c, http.StatusInternalServerError, gin.H{"error": err.Error()})
				return
			}
			c.Data(http.StatusOK, "image/jpeg", imageData)
		})
// 获取后将照片上传到服务器
DaHuaMonitor.GET("/DaHua", func(c *gin.Context) {
			channel := c.DefaultQuery("channel", "9")
			url := c.Query("url")
			username := c.Query("username")
			password := c.Query("password")

			// 获取摄像头照片
			imageData, err := GetCameraSnapshot(url, channel, username, password)
			if err != nil {
				ReturnError(c, http.StatusInternalServerError, gin.H{"error": err.Error()})
				return
			}

			// 上传照片到服务器
			imageURL, err := UploadImage(imageData)
			if err != nil {
				ReturnError(c, http.StatusBadRequest, gin.H{"error": err.Error()})
				return
			}

			ReturnSuccess(c, http.StatusOK, 1, gin.H{"url": imageURL}, "上传成功")
		})

调用接口

/DaHua:

1749538290795.jpg

/Test:

1749538482155.jpg

WinForm

代码

using System;
using System.Drawing;
using System.Net.Http;
using System.Windows.Forms;

namespace DAHUA_Test
{
    public partial class MainForm : Form
    {
        // 声明 HttpClient 作为成员变量以便重用
        private HttpClient _httpClient;

        public MainForm()
        {
            InitializeComponent();
            picBox.SizeMode = PictureBoxSizeMode.Zoom;
            TBUrl.Enabled = false;
        }

        private void BtnFetch_Click(object sender, EventArgs e)
        {
            if (this.ValidateChildren())
            {
                FetchDaHuaSnapshot(TBUrl.Text, TBChannel.Text);
            }
            else
            {
                Console.WriteLine("表单验证未通过");
            }
        }

        private void TBChannel_Validating(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (string.IsNullOrEmpty(TBChannel.Text) || !int.TryParse(TBChannel.Text, out int value) || value <= 0)
            {
                errorProvider1.SetError(TBChannel, "请输入大于0的正整数");
                e.Cancel = true; // 可选:阻止离开控件直到输入有效
            }
            else
            {
                errorProvider1.SetError(TBChannel, null); // 清除错误
            }
        }

        private async void FetchDaHuaSnapshot(string url, string channel)
        {
            try
            {
                if (string.IsNullOrEmpty(channel) || !int.TryParse(channel, out int value) || value <= 0)
                {
                    MessageBox.Show("channel 必须为大于0的正整数", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return;
                }

                // 禁用按钮防止重复点击
                btnFetch.Enabled = false;
                lblStatus.Text = "正在连接摄像头...";
                picBox.Image = null; // 清除旧图片

                // 如果HttpClient未初始化,则创建它
                if (_httpClient == null)
                {
                    var handler = new HttpClientHandler
                    {
                        PreAuthenticate = true,
                        Credentials = new System.Net.NetworkCredential("admin", "admin123")
                    };

                    _httpClient = new HttpClient(handler)
                    {
                        Timeout = TimeSpan.FromSeconds(15) // 设置15秒超时
                    };
                }

                string apiUrl = $"{url}?channel={channel}";

                lblStatus.Text = "正在获取快照...";

                using (var response = await _httpClient.GetAsync(apiUrl))
                {
                    // 检查响应状态
                    response.EnsureSuccessStatusCode();

                    // 读取响应内容为流
                    using (var stream = await response.Content.ReadAsStreamAsync())
                    {
                        // 从流创建图像
                        var image = Image.FromStream(stream);

                        // 在PictureBox中显示图像
                        picBox.Image = image;
                        lblStatus.Text = $"快照获取成功 {DateTime.Now:HH:mm:ss}";
                    }
                }
            }
            catch (HttpRequestException ex)
            {
                lblStatus.Text = "网络错误: " + ex.Message;
                MessageBox.Show($"网络请求失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            catch (Exception ex)
            {
                lblStatus.Text = "错误: " + ex.Message;
                MessageBox.Show($"发生错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                btnFetch.Enabled = true;
            }

        }
    }
}

预览

1749612126084.png

总结

直接调用DAHUA SDK接口,添加参数channel,并进行Digest Auth验证即可。或是在获取到快照后将照片上传至服务器,再返回图片的网络地址亦可。

附件

大华SDK:DAHUA_API.pdf

联系作者

微信:
wechat1.jpg

QQ:

qq1.jpg