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

入门级别的一段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代理
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论 编辑记录
查看更多回答(6条)

报告相同问题?

问题事件

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

悬赏问题

  • ¥60 如何批量获取json的url
  • ¥15 对法兰连接元件所承受的表面载荷等效转化为法兰开孔接触面上的等效表面载荷?
  • ¥15 comsol仿真压阻传感器
  • ¥15 Python线性规划函数optimize.linprog求解为整数
  • ¥15 llama3中文版微调
  • ¥15 pg数据库导入数据序列重复
  • ¥15 三分类机器学习模型可视化分析
  • ¥15 本地测试网站127.0.0.1 已拒绝连接,如何解决?(标签-ubuntu)
  • ¥50 Qt在release捕获异常并跟踪堆栈(有Demo,跑一下环境再回答)
  • ¥30 python,LLM 文本提炼