代码敲上天. 2023-06-07 21:21 采纳率: 58.3%
浏览 119
已结题

入门级别的一段VUE前端拍照像后端发送请求的代码,帮排错

整体思路是前端的takephoto方法拍照调用全局ajax请求函数,像后端发送post,postman测过后端没问题,怀疑takephoto方法有问题

img

img


后端已写跨域解决代码,不知为什么无效

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH")
                .maxAge(3600);
    }
}


<!-- 这里是html模板 -->
<template>
    <div class="page">
        <!-- //el-row 组件设置了 type="flex",表示使用弹性布局,子元素会按比例自适应屏幕大小;
        justify="center" 表示子元素在横向上居中对齐;align="middle" 表示子元素在竖向上居中对齐 -->
        <el-row type="flex" justify="center" align="middle" class="container">
            <!-- :lg="14" 和 :xl="10" 属性表示在不同的屏幕尺寸下,该元素会占据不同的列数。
            同时,使用 class="hidden-md-and-down" 属性可以隐藏该元素在中等及以下屏幕尺寸下的显示,
            从而实现响应式布局 -->
            <el-col :lg="14" :xl="10" class="hidden-md-and-down">
                <el-row class="panel">
                    <!-- span 属性表示该列占据的网格数,可以是 1 到 24 之间的整数。例如,:span="12" 表示该列占据了当前行的一半宽度。 -->
                    <el-row :span="12">
                        <div class="left">

                            <img src="../assets/login/logo.png" class="logo" />
                            <img src="../assets/login/big-1.png" class="big" />
                        </div>
                    </el-row>


                    <el-row :span="12">
                        <div class="right">
                            <div class="title-container">
                                <h2>基于人脸识别的在线办公平台</h2>
                            </div>
                            <div v-if="modeCode==1">
                                <div class="row" >
                                    <el-input v-model="username" placeholder="用户名" prefix-icon="el-icon-user" clearable></el-input>
                                </div>
                                <div class="row">
                                    <el-input type="password" v-model="password" placeholder="密码" prefix-icon="el-icon-lock" clearable></el-input>
                                </div>
                                <div class="row">
                                    <el-button type="primary" class="btn" @click="login">登录</el-button>
                                </div>
                                <div class="row">
                                    <el-button type="primary" class="btn" @click="bx2">人脸识别</el-button>
                                </div>
                                <div class="row">
                                    <el-button type="primary" class="btn" @click="changeMode">扫码登录</el-button>
                                </div>


                            </div>
                            <div v-if="modeCode==2">
                                <div class="qrCode-container">
                                    <img :src="qrCode" height="153" class="qrCode" />
                                    <img src="../assets/login/phone.png" height="148" />
                                </div>
                                <div class="row">
                                    <el-button type="primary" class="btn" @click="bx1">其他登陆</el-button>
                                </div>
                            </div>
                            <div v-if="modeCode==3">
                                <div class="video-container">
                                    <video id="videoCamera" class="camera-video" :width="videoWidth" :height="videoHeight" autoplay></video>
                                    <canvas id="canvasCamera" style="display:none;" :width="videoWidth" :height="videoHeight"></canvas>
                                    <img v-if="imageUrl" :src="imageUrl" class="photo-preview">
                                </div>
                                <div class="controls">
                                    <el-button type="primary"   @click="openCamera">打开摄像头</el-button>
                                    <el-button type="primary"   @click="takePhotoHandle" :disabled="isPlaying">拍照登录</el-button>
                                    <el-button type="primary"  @click="bx1">返回</el-button>
                                </div>
                            </div>
                        </div>
                    </el-row>
                </el-row>
            </el-col>
        </el-row>
    </div>
</template>

<!-- 这里是js代码 -->
<script>
    // 如果要用外部的需要在这里导入,和以前js一样
    import 'element-plus/lib/theme-chalk/display.css';
    import {
        isUsername,
        isPassword
    } from '../utils/validate.js';
    import router from '../router/index.js';
    // 这里是初始绑定数据
    export default { //导出默认对象、函数或值会在模块加载时被加载到内存中,以供其他模块使用
        data: function() { //值,值会自动渲染,当组件被渲染到页面中时,Vue.js 会自动将 data 中定义的数据绑定到相应的 DOM 元素上,以便在需要时获取和修改数据
            return {
                loading: false, // 上传照片的loading
                videoWidth: 350,
                videoHeight: 188,
                username: null,
                password: null,
                modeCode: 1,
                imgSrc: "", // 照片地址
                photoVideo: null, // 拍照框
                photoContext: null, // canvas绘图环境
                photoCancas: null, // 预览框
                downloadButton: false,
                qrCode: '',
                uuid: null,
                qrCodeTimer: null,
                loginTimer: null
            };
        },
        methods: { //函数
            login: function() {
                let that = this;
                if (!isUsername(that.username)) {
                    // Element UI 中的消息提示组件 $message,用于在页面上显示一条消息提示。
                    // 具体来说,$message 是一个全局的方法,
                    // 可以通过 this.$message 或者 that.$message 的方式在组件中使用。
                    that.$message({
                        message: '用户名格式不正确',
                        type: 'error',
                        duration: 1200
                    });
                } else if (!isPassword(that.password)) {
                    that.$message({
                        message: '密码格式不正确',
                        type: 'error',
                        duration: 1200
                    });
                } else {
                    //准备数据发送ajax请求
                    // 这样做的目的是将用户输入的数据封装到一个对象中,方便后续的处理和传递。在发送登录请求时,
                    // 使用该对象作为请求参数,可以直接将用户名和密码发送到后端进行验证。
                    // 另外,使用 let 关键字定义的变量具有块级作用域,只在当前代码块中有效。
                    // 这样可以避免变量污染和命名冲突的问题,提高代码的健壮性和可维护性。
                    let data = {
                        username: that.username,
                        password: that.password
                    };
                    //发送登陆请求:参数分别是路径,请求方式,同步异步,回调函数
                    that.$http('user/login', 'POST', data, true, function(resp) {
                        if (resp.result) { //根据后端的rusult判断
                            //在浏览器的storage中存储用户权限列表,这样其他页面也可使用storage中的数据,实现共享
                            let permissions = resp.permissions;
                            //取出Token令牌,保存到storage中,因为新版本浏览器不支持crendential,我们手动存储
                            let token = resp.token;
                            localStorage.setItem('permissions', permissions); //存储到浏览器的 localStorage 中
                            localStorage.setItem('token', token);
                            //让路由跳转页面,这里的Home是home.vue页面的名字
                            router.push({
                                name: 'Home'
                            });
                        } else {
                            that.$message({
                                message: '登陆失败',
                                type: 'error',
                                duration: 1200
                            });
                        }
                    });
                }
            },
            async openCamera() {
                this.photoVideo = document.getElementById('videoCamera')
                this.photoCancas = document.getElementById('canvasCamera')
                this.photoContext = this.photoCancas.getContext('2d')
                try {
                    const constraints = {
                        audio: false,
                        video: {
                            width: this.videoWidth,
                            height: this.videoHeight
                        }
                    }
                    // 这段代码使用 WebRTC 的 navigator.mediaDevices.getUserMedia() 方法获取用户的媒体设备(例如摄像头和麦克风)的媒体流。
                    const stream = await navigator.mediaDevices.getUserMedia(constraints)
                    this.photoVideo.srcObject = stream //把媒体流给video的来源
                    this.photoVideo.play()
                } catch (error) {
                    this.$message({
                        title: '警告',
                        message: '请确认摄像头能正常工作,必须使用谷歌浏览器或者360浏览器的极速模式,否则拍照不能正常使用',
                        type: 'warning',
                        duration: 8000
                    });
                }
            },
            takePhotoHandle() {
                this.photoContext.drawImage(this.photoVideo, 0, 0, this.videoWidth, this.videoHeight)
                this.photoCancas.toBlob((blob) => {
                    const file = new File([blob], 'face.jpg', {
                        type: 'image/jpeg',
                        lastModified: Date.now()
                    })
                    const formData = new FormData()
                    formData.append('file', file)
                    let that = this;
                    that.$sftp("face/login", "POST", formData, true, function(resp) {
                        that.$message({
                            message: '操作成功',
                            type: 'success',
                            duration: 1200,
                        });
                        router.push({
                            name: 'Home'
                        });
                    })
                })
            },
            bx2: function() {
                let that = this;
                that.modeCode = 3;
            },
            bx1: function() {
                let that = this;
                that.modeCode = 1;
                stopNavigator();
            },
            stopNavigator: function(){
                  that.photoVideo.srcObject.getTracks()[0].stop()
            },
            changeMode: function() {
                let that = this;
                that.modeCode = 2;
                //加载二维码图片
                if (that.modeCode == 2) {
                    that.loadQRCode();
                    //创建刷新二维码定时器
                    that.qrCodeTimer = setInterval(function() {
                        that.loadQRCode();
                    }, 5 * 60 * 1000);
                    that.loginTimer = setInterval(function() {
                        that.$http('user/wechatLogin', 'POST', {
                            uuid: that.uuid
                        }, true, function(resp) {
                            if (resp.result) {
                                clearInterval(that.qrCodeTimer);
                                clearInterval(that.loginTimer);
                                let permissions = resp.permissions;
                                localStorage.setItem('permissions', permissions);
                                router.push({
                                    name: 'Home'
                                });
                            }
                        });
                    }, 5000);
                } else {
                    //销毁刷新二维码定时器
                    clearInterval(that.qrCodeTimer);
                    clearInterval(that.loginTimer);
                }
            },
            //加载二维码图片的封装方法
            loadQRCode: function() {
                this.$http('user/createQrCode', 'GET', null, true, resp => {
                    this.qrCode = resp.pic;
                    this.uuid = resp.uuid;
                });
            }
        }
    };
</script>

<!-- 这里是css区域,当然可以不用写到这里,可以用引入的方法 -->
<style lang="less" scoped="scoped">
    @import url('login.less');
</style>


app.config.globalProperties.$sftp = function(url, method, mydata, async, fun) {
    $.ajax({
        url: baseUrl + url,
        dataType: 'json',
        type: method,
        contentType: "multipart/form-data",
        xhrFields: {
            withCredentials: true
        },
        headers: {
            "token": localStorage.getItem("token"),
        },
        async: async,
        data: mydata,
        success: function(resp) {
            if (resp.code == 200) {
                fun(resp)
            } else {
                ElMessage.error({
                    message: resp.msg,
                    duration: 1200
                });
            }
        },
        error: function(e) {
            if (e.status == undefined) {
                ElMessage.error({
                    message: "前端页面错误",
                    duration: 1200
                });
            } else {
                let status = e.status
                if (status == 401) {
                    router.push({
                        name: 'Login'
                    })
                } else {
                    ElMessage.error({
                        message: e.responseText,
                        duration: 1200
                    });
                }
            }

        }
    })
}

后端代码


@RestController
@RequestMapping("/face")
@Slf4j
@Tag(name = "FaceController", description = "人脸识别接口")//在swagger文档的描述
public class FaceController {
    @Autowired
    private FaceService faceService;
    @PostMapping("/login")
    @Operation(summary = "人脸登录")
    public R detectFace(@RequestParam("file") MultipartFile face) throws Exception {
        byte[] buff = face.getBytes();
        JSONObject jsonObject = JSONUtil.parseObj(faceService.detectFace(buff));
        if (jsonObject.containsKey("faces")) {
            JSONObject faceToken = jsonObject.getJSONArray("faces").getJSONObject(0);
            return R.ok().put("face_token", faceToken.getStr("face_token"));
        }
        return R.error().put("result","失败了");
    }

  • 写回答

7条回答 默认 最新

  • 游一游走一走 2023-06-08 09:17
    关注
    1. 你后端的跨域设置有问题,修改后再尝试下
      @Configuration
      public class CorsConfig implements WebMvcConfigurer {
       @Override
       public void addCorsMappings(CorsRegistry registry) {
           registry.addMapping("/**")
                   .allowedOriginPatterns("*")
                   .allowCredentials(true)
                   .allowedMethods("GET","POST","OPTIONS")
                   .maxAge(3600);
       }
      }
      
    2. 如果你的前后端是部署在一个IP/域名下,开发时请不要后端开启跨域设置,请使用vue的proxy代理
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论 编辑记录
  • 全栈若城 Python领域新星创作者 2023-06-07 21:33
    关注

    请求接口的状态 以及 preview 截图看下

    评论
  • 朱友斌 2023-06-08 08:35
    关注

    你这明明是跨域问题了,让后端处理一下跨域问题。

    评论
  • qq_37749055 2023-06-08 11:31
    关注

    vue 走代理试试

    评论 编辑记录
  • 技术宅program 2023-06-08 12:13
    关注

    害,后端搞什么跨域呀,走代理就可以了

    评论
  • Android西红柿 2023-06-08 14:07
    关注

    解决跨域问题,或者通过设置代理服务器,将跨域请求转发到同一域名下的服务器,再由服务器返回响应。

    评论
  • Answer348 2023-06-10 14:13
    关注

    回答部分参考、引用ChatGpt以便为您提供更准确的答案: 根据您提供的要求,我将尽力回答您看到的问题。

    问题1: 根据图示,题目中给出了一道数学方程,需要求解x的值。方程的左侧是一个根式表达式,右侧是一个整数。要求解该方程,我们需要将根式进行化简,并根据等式的性质进行变形。具体步骤如下:

    1. 首先,将根式进行化简,即将根号内的数进行分解。根据乘法的性质,√(a * b) = √a * √b,我们可以得到 √(x + 2) * √(x + 5) = 2 * √2。
    2. 接下来,我们将方程两边进行平方,消去根号。平方的性质是 (a * b)^2 = a^2 * b^2,所以得到 (x + 2) * (x + 5) = 4 * 2。
    3. 将方程进行展开,得到 x^2 + 7x + 10 = 8。
    4. 继续化简方程,将8移到左侧,得到 x^2 + 7x + 10 - 8 = 0。
    5. 化简得到 x^2 + 7x + 2 = 0。
    6. 最后,我们可以使用因式分解、配方法或求根公式等方法来解这个二次方程,以求得x的值。

    问题2: 根据图示,题目中给出了一个数列,要求找出这个数列的规律并填入括号中的数字。观察数列,我们可以发现规律是每一项都比前一项的平方小1。具体步骤如下:

    1. 数列的第一项为 3,填入括号中。
    2. 第二项为 3^2 - 1 = 8。
    3. 第三项为 8^2 - 1 = 63。
    4. 第四项为 63^2 - 1 = 3968。
    5. 将结果依次填入括号中,得到数列 3, 8, 63, 3968, (3968^2 - 1)。
    评论
查看更多回答(6条)

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 6月12日
  • 已采纳回答 6月8日
  • 修改了问题 6月8日
  • 修改了问题 6月7日
  • 展开全部

悬赏问题

  • ¥15 利用加权最小二乘法求亚马逊各类商品的价格指标?怎么求?
  • ¥15 c++ word自动化,为什么可用接口是空的?
  • ¥15 Matlab计算100000*100000的矩阵运算问题:
  • ¥50 VB6.0如何识别粘连的不规则的数字图片验证码
  • ¥16 需要完整的这份订单所有的代码,可以加钱
  • ¥30 写一个带界面控制的机房电脑一键开机关机并且实时监控的软件
  • ¥15 Stata数据分析请教
  • ¥15 请教如何为VS2022搭建 Debug|win32的openCV环境?
  • ¥15 关于#c++#的问题:c++如何使用websocketpp实现websocket接口调用,求示例代码和相关资料
  • ¥15 51单片机的外部中断,按下按键后不能切换到另一个模式