You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QR Code扫码本地正常,公网服务器无法使用,求排查修复方案

问题分析与解决方案

核心原因:浏览器安全上下文限制

现代浏览器对getUserMedia(访问摄像头/麦克风)有严格的安全规则:

  • 仅允许在安全上下文中调用该API
  • localhost属于浏览器默认信任的安全上下文,因此本地测试可以正常运行
  • 公网IP如果使用HTTP协议(非HTTPS),会被判定为非安全上下文,直接阻止摄像头访问请求

解决方案步骤

1. 配置服务器启用HTTPS(关键)

这是解决问题的核心,必须将公网部署的服务切换到HTTPS协议:

  • 申请免费SSL证书(如Let's Encrypt)
  • 在服务器(Nginx/Apache/Node.js等)中配置证书,强制HTTPS访问
  • 确保所有页面资源(JS、CSS、图片等)都通过HTTPS加载,避免混合内容错误

2. 代码层面优化(辅助排查与兼容)

现有代码逻辑无本质问题,但可增强错误处理,方便定位问题:

static getCameras() {
    return new Promise((resolve, reject) => {
        // 先检查是否在安全上下文
        if (!window.isSecureContext) {
            this._log("当前环境非安全上下文,无法访问摄像头");
            reject("请使用HTTPS协议访问以启用摄像头功能");
            return;
        }

        // Check if necessary media-related features are supported by the browser.
        if (
            navigator.mediaDevices &&
            navigator.mediaDevices.enumerateDevices &&
            navigator.mediaDevices.getUserMedia
        ) {
            this._log("navigator.mediaDevices used");

            // Use getUserMedia to access the camera.
            navigator.mediaDevices.getUserMedia({ audio: false, video: true })
                .then(stream => {
                    // Hacky approach to close any active stream if they are active.
                    stream.oninactive = _ => this._log("All streams closed");

                    const closeActiveStreams = stream => {
                        const tracks = stream.getVideoTracks();

                        // Disable and stop each track in the stream.
                        for (var i = 0; i < tracks.length; i++) {
                            const track = tracks[i];
                            track.enabled = false;
                            track.stop();
                            stream.removeTrack(track);
                        }
                    }

                    // Enumerate available devices to find video input devices.
                    navigator.mediaDevices.enumerateDevices()
                        .then(devices => {
                            const results = [];
                            for (var i = 0; i < devices.length; i++) {
                                const device = devices[i];
                                if (device.kind == "videoinput") {
                                    results.push({
                                        id: device.deviceId,
                                        label: device.label
                                    });
                                }
                            }
                            this._log(`${results.length} results found`);
                            closeActiveStreams(stream);
                            resolve(results);
                        })
                        .catch(err => {
                            this._log(`枚举设备失败: ${err.name} : ${err.message}`);
                            reject(`${err.name} : ${err.message}`);
                        });
                })
                .catch(err => {
                    this._log(`获取摄像头权限失败: ${err.name} : ${err.message}`);
                    reject(`${err.name} : ${err.message}`);
                });
        } else if (MediaStreamTrack && MediaStreamTrack.getSources) {
            // Use MediaStreamTrack.getSources as an alternative method for older browsers.
            this._log("MediaStreamTrack.getSources used");

            const callback = sourceInfos => {
                const results = [];
                for (var i = 0; i !== sourceInfos.length; ++i) {
                    const sourceInfo = sourceInfos[i];
                    if (sourceInfo.kind === 'video') {
                        results.push({
                            id: sourceInfo.id,
                            label: sourceInfo.label
                        });
                    }
                }
                this._log(`${results.length} results found`);
                resolve(results);
            }

            // Invoke MediaStreamTrack.getSources to obtain video sources.
            MediaStreamTrack.getSources(callback);
        } else {
            // If neither method is supported, log an error and reject the Promise.
            this._log("unable to query supported devices.");
            reject("unable to query supported devices.");
        }
    });
}

优化点说明:

  • 新增安全上下文检查,提前抛出明确错误提示
  • 给每个异常捕获块添加详细日志,便于排查具体失败场景

3. 额外排查点

  • 打开浏览器开发者工具(F12)查看控制台错误,如果出现NotAllowedError或SecurityError,可确认是HTTPS相关问题
  • 确保用户访问公网服务时,允许了浏览器的摄像头权限请求
  • 部分浏览器对纯IP地址的HTTPS访问有额外限制,建议绑定域名后通过域名访问

内容的提问来源于stack exchange,提问作者Jonjon Candare

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 05:45:57