使用虹软SDK实现离线人脸注册,人脸登录(H5-JS前端,java后台)

简介: 一开始找人脸识别的第三方接口,选择了百度,就是发请求给百度的接口,解析人家返回的数据。

前言:


一开始找人脸识别的第三方接口,选择了百度,就是发请求给百度的接口,解析人家返回的数据。


但是这样的话,如果没有网络,或者没有外网。程序在局域网中使用的时候,自然就gg了。


人脸识别嘛,大家了解的最多的就是现在手机自带的人脸识别,这个肯定不会说在你没有网络的情况下用不了。


然后就找离线人脸识别吧,就发现虹软这个可以把算法下载到本地……


第一步,获取SDK

首先注册开发者账号,创建一个应用,得到两个东西,用于激活SDK引擎的,SDK key 和 AppID。


百度搜索“虹软”,进入官网,点击右上角开放平台下的人脸识别SDK,如图:

然后进入如下页面点击右上角开发者中心新建应用,获取SDK之前,还有不可或缺的一步……实名认证

提交完资料一般两个小时之内就有结果了。

-

创建完应用点击这个下载按钮把SDK下载到本地,我这里选择的是java版本的,我也不会其他的…… 下载完后解压,就是这样一个文件夹

文件夹中的doc文件夹中的一个pdf文档,就是开发者文档了。

文件夹中包括开发者文档、API文档、示例程序、jar包、引擎库dll文件。

文档中有对这个文件夹结构的详细介绍,这里就不重复了。

第二步,写后台

springboot项目,maven管理,项目结构:把文件夹中的jar包复制到项目中的lib(自己创建的)文件夹下,

依赖这样写:

----我这里用的是SQLserver

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.0.3.RELEASE</version>
</parent>
<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>3.8.1</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
  <dependency>
         <groupId>org.mybatis.spring.boot</groupId>
         <artifactId>mybatis-spring-boot-starter</artifactId>
         <version>1.3.5</version>
     </dependency>
     <dependency>
    <groupId>com.microsoft.sqlserver</groupId>
    <artifactId>sqljdbc4</artifactId>
    <version>4.0</version>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
  </dependency>
  <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>1.3.5</version>
    </dependency>
  <dependency>
    <groupId>com.arcsoft.face</groupId>
    <artifactId>arcsoft-sdk-face</artifactId>
    <version>2.2.0.1</version>
    <scope>system</scope>
    <systemPath>${basedir}/lib/arcsoft-sdk-face-2.2.0.1.jar</systemPath>
  </dependency>
</dependencies>
<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <configuration>
        <includeSystemScope>true</includeSystemScope>
      </configuration>
    </plugin>
  </plugins>
  <finalName>face</finalName>
</build>

还要创建一个temp文件夹存放临时生成的图片文件。


启动类就不贴了,下面这个是引擎激活类:


我放在了启动包下,这个类要单独运行一遍,用来激活在这个设备的引擎,如果换了设备还要再运行激活一遍


/**
 * 设备引擎激活
* @author zsz
* @version 
* @创建时间:2019年10月29日 下午2:51:24
*/
public class FaceActivation{
  public static void main(String[] args) {
        //填自己的AppID和SDKkey
  String appId = "";
        String sdkKey = "";
        //dll文件所在目录
        FaceEngine faceEngine = new FaceEngine("D:\\人脸识别\\ArcSoft_ArcFace_Java_Windows_x64_V2.2\\libs\\WIN64");
        //激活引擎
        int activeCode = faceEngine.activeOnline(appId, sdkKey);
        if (activeCode != ErrorInfo.MOK.getValue() && activeCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
            System.out.println("引擎激活失败");
        }else {
          System.out.println("引擎激活成功");
        }
  }
}

Utils包里面是一个base64跟图片互转的工具类,反正不是自己写的……


import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class Base64Utils {
     /**
      * 图片转化成base64字符串
      * @param imgPath
      * @return
      */
     public static String GetImageStr(String imgPath) {// 将图片文件转化为字节数组字符串,并对其进行Base64编码处理
         String imgFile = imgPath;// 待处理的图片
         InputStream in = null;
         byte[] data = null;
         String encode = null; // 返回Base64编码过的字节数组字符串
         // 对字节数组Base64编码
         BASE64Encoder encoder = new BASE64Encoder();
         try {
             // 读取图片字节数组
             in = new FileInputStream(imgFile);
             data = new byte[in.available()];
             in.read(data);
             encode = encoder.encode(data);
         } catch (IOException e) {
             e.printStackTrace();
         } finally {
             try {
                 in.close();
             } catch (IOException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
             }
         }
         return encode;
     }
     /**
      * base64字符串转化成图片
      * 
      * @param imgData
      *            图片编码
      * @param imgFilePath
      *            存放到本地路径
      * @return
      * @throws IOException
      */
     @SuppressWarnings("finally")
     public static boolean GenerateImage(String imgData, String imgFilePath) throws IOException { // 对字节数组字符串进行Base64解码并生成图片
         if (imgData == null) // 图像数据为空
             return false;
         BASE64Decoder decoder = new BASE64Decoder();
         OutputStream out = null;
         try {
             out = new FileOutputStream(imgFilePath);
             // Base64解码
             byte[] b = decoder.decodeBuffer(imgData);
             for (int i = 0; i < b.length; ++i) {
                 if (b[i] < 0) {// 调整异常数据
                     b[i] += 256;
                 }
             }
             out.write(b);
         } catch (FileNotFoundException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         } catch (IOException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         } finally {
             out.flush();
             out.close();
             return true;
         }
     }
  }

实体类包pojo,一个User类,跟数据库的表是对应的,


在SQLserver中的表设计是这样的


public class User {
  //用户名
  private String username;
  //用户人脸特征
  private byte[] extract;
  public User(String username, byte[] extract) {
  super();
  this.username = username;
  this.extract = extract;
  }
  public String getUsername() {
  return username;
  }
  public void setUsername(String username) {
  this.username = username;
  }
  public byte[] getExtract() {
  return extract;
  }
  public void setExtract(byte[] extract) {
  this.extract = extract;
  }
}

face包里面有两个类,一个获取人脸特征的,一个人脸特征对比的。


获取人脸特征:


public class MyFaceFeature {
  public byte[] extract(File file,FaceEngine faceEngine) {
  //人脸检测
        ImageInfo imageInfo = getRGBData(file);
        List<FaceInfo> faceInfoList = new ArrayList<FaceInfo>();
        int detectCode = faceEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), ImageFormat.CP_PAF_BGR24, faceInfoList);
        if(faceInfoList == null || faceInfoList.size()==0) {
          return null;
        }
        //人脸属性检测
        FunctionConfiguration configuration = new FunctionConfiguration();
        configuration.setSupportAge(true);
        configuration.setSupportFace3dAngle(true);
        configuration.setSupportGender(true);
        configuration.setSupportLiveness(true);
        int processCode = faceEngine.process(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), ImageFormat.CP_PAF_BGR24, faceInfoList, configuration);
        //3D信息检测
        List<Face3DAngle> face3DAngleList = new ArrayList<Face3DAngle>();
        int face3dCode = faceEngine.getFace3DAngle(face3DAngleList);
        if(face3DAngleList == null || face3DAngleList.size() == 0) {
          return null;
        }
        System.out.println("3D角度:" + face3DAngleList.get(0).getPitch() + "," + face3DAngleList.get(0).getRoll() + "," + face3DAngleList.get(0).getYaw());
        //活体检测
        List<LivenessInfo> livenessInfoList = new ArrayList<LivenessInfo>();
        int livenessCode = faceEngine.getLiveness(livenessInfoList);
        System.out.println("活体:" + livenessInfoList.get(0).getLiveness());
        //只有等于1才是活体,-1是没有人脸,0是非活体(照片之类的),2是多张人脸
        if(livenessInfoList.get(0).getLiveness()!=1) {
          return null;
        }
        //特征提取
        FaceFeature faceFeature = new FaceFeature();
        int extractCode = faceEngine.extractFaceFeature(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), ImageFormat.CP_PAF_BGR24, faceInfoList.get(0), faceFeature);
        //返回特征值
  return faceFeature.getFeatureData();
  }
}

人脸特征对比:

public class FaceContrast {
  public float contrast(byte[] face1,byte[] face2,FaceEngine faceEngine) {
   //特征比对
        FaceFeature targetFaceFeature = new FaceFeature();
        targetFaceFeature.setFeatureData(face1);
        FaceFeature sourceFaceFeature = new FaceFeature();
        sourceFaceFeature.setFeatureData(face2);
        FaceSimilar faceSimilar = new FaceSimilar();
        int compareCode = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
        //返回相似度
        return faceSimilar.getScore();
  }
}

到这里就剩下controller层service层和dao层了。


controller层:


一个注册一个登录


@RestController
@RequestMapping("face")
@CrossOrigin
public class FaceController {
  FaceEngine faceEngine = new FaceEngine();
  {
  // 引擎配置
  EngineConfiguration engineConfiguration = new EngineConfiguration();
  //检测模式 图片/视频,这里选择的是图片模式
  engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
  //人脸检测角度
  engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_0_ONLY);
  // 功能配置
  FunctionConfiguration functionConfiguration = new FunctionConfiguration();
  //是否支持年龄检测
  functionConfiguration.setSupportAge(true);
  //是否支持3D人脸检测
  functionConfiguration.setSupportFace3dAngle(true);
  //是否支持人脸检测
  functionConfiguration.setSupportFaceDetect(true);
  //是否支持人脸识别
  functionConfiguration.setSupportFaceRecognition(true);
  //是否支持性别检测
  functionConfiguration.setSupportGender(true);
  //是否支持RGB活体检测
  functionConfiguration.setSupportLiveness(true);
  //是否支持RGB活体检测
  functionConfiguration.setSupportIRLiveness(true);
  //设置引擎功能配置
  engineConfiguration.setFunctionConfiguration(functionConfiguration);
  // 初始化引擎
  int initCode = faceEngine.init(engineConfiguration);
  if (initCode != ErrorInfo.MOK.getValue()) {
    System.out.println("初始化引擎失败");
  }
  }
  @Autowired
  private FaceService service;
  @RequestMapping("add")
  public String faceAdd(String user_id, String base) {
  // 去掉base64编码中的图片头信息
  String base64 = base.split(",")[1];
  try {
    //base64转图片
    Base64Utils.GenerateImage(base64, "./temp/temp.jpg");
  } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
  }
  //获取人脸特征
  byte[] extract = new MyFaceFeature().extract(new File("./temp/"+user_id+"temp.jpg"), faceEngine);
  if(extract == null) {
    return "{\"msg\":\"检测人脸失败\"}";
  }
  //把人脸特征报错到数据库
  return service.faceAdd(new User(user_id,extract))?"{\"msg\":\"注册成功\"}":"{\"msg\":\"注册失败\"}";
  }
  @RequestMapping("login")
  public String login(String base) {
  // 去掉base64编码中的图片头信息
  String base64 = base.split(",")[1];
  try {
    Base64Utils.GenerateImage(base64, "./temp/temp.jpg");
  } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
  }
  //获取当前人脸特征
  byte[] extract = new MyFaceFeature().extract(new File("./temp/temp.jpg"), faceEngine);
  if(extract==null) {
    return null;
  }

 //查询所有人脸 一一对比

 人脸对比对象

 FaceContrast faceContrast = new FaceContrast();

 获取所有注册过的用户

 List<User> userList = service.getAllUserFace();

 循环对比

for(User user : userList) {
    //如果大于0.8就表示是同一人
    if(faceContrast.contrast(extract, user.getExtract(), faceEngine)>0.8) {
    System.out.println("成功");
    return "{\"user_id\":\""+user.getUsername()+"\"}";
    }
  }
  System.out.println("失败");
  return null;
  }
}

server包 ,service层:


没有业务,直接传给下一层


@Service
public class FaceService {
  @Autowired
  private FaceMapper mapper;
  //注册人脸
  public Boolean faceAdd(User user) {
  return mapper.add(user);
  }
  //查找人脸
  public List<User> getAllUserFace() {
  return mapper.getAllUser();
  }
}
mapper包,dao层
@Mapper
public interface FaceMapper {
  @Insert("insert into user_face values(#{username},#{extract})")
  Boolean add(User user);
  @Select("select * from user_face")
  List<User> getAllUser();
}

后台就是这些,注释写的比较详细了


在启动项目的时候会报错,no libarcsoft_face_engine_jni in java.library.path


见另一个博客:使用虹软SDK的 时候启动项目出现no libarcsoft_face_engine_jni in java.library.path_阿演的博客-CSDN博客


接下来就是前端


一个index页面和,两个按钮,跳转到注册和登录


页面:


<!DOCTYPE html>
<html>
  <head>
  <meta charset="UTF-8">
  <title>主页</title>
  </head>
  <body>
  <button id="register" style="width: 700px;height: 100px;">注册</button>
  <button id="login" style="width: 700px;height: 100px;">登录</button>
  </body>
  <script src="js/index.js"></script>
</html>
JS:
document.getElementById("register").onclick = function(){
  window.location.href = "register.html";
}
document.getElementById("login").onclick = function(){
  window.location.href = "login.html";
}
 注册页面register
页面:
<!DOCTYPE html>
<html>
  <head>
  <meta charset="utf-8" />
  <title>人脸注册</title>
  </head>
  <body>
  <video id="video" src="" autoplay="autoplay" loop="loop" muted="muted"></video>
  <canvas id="canvas" style="border:1px solid #d3d3d3;"></canvas>
  <br />
  <input type="text" id="user_id" placeholder="请输入账号" style="width: 700px; height: 100px; font-size: 40px;"/>
  <div id="support"></div>
  <button id="ok" style="width: 700px;height: 100px; font-size: 40px;">确认</button>
  <div id="msg" style="font-size: 50px;"></div>
  </body>
  <script src="js/register.js"></script>
  <script src="js/jquery-1.8.2.min.js"></script>
</html>
JS:
//判断浏览器是否支持HTML5 Canvas
window.onload = function() {
  try {
  //动态创建一个canvas元 ,并获取他2Dcontext。如果出现异常则表示不支持 
  document.createElement("canvas").getContext("2d");
  } catch(e) {
  document.getElementByIdx("support").innerHTML = "浏览器不支持HTML5 CANVAS";
  }
}
//这段代 主要是获取摄像头的视频流并显示在Video 签中
window.addEventListener("DOMContentLoaded", function() {
  var canvas = document.getElementById("canvas"),
  context = canvas.getContext("2d"),
  img = document.getElementById("img"),
  video = document.getElementById("video"),
  videoObj = {
    "video": true
  },
  errBack = function(error) {
    console.log("Video capture error: ", error.code);
    alert("不支持");
  };
  canvas.width = 450;
  canvas.height = 600;
  var button = document.getElementById("ok");
  button.onclick = function(){
  context.drawImage(video, 0, 0, 450, 600);
  // 把画布的内容转换为base64编码格式的图片
     var data = canvas.toDataURL( 'image/png', 1 );  //1表示质量(无损压缩)
  var user_id = document.getElementById("user_id").value;
  document.getElementById("msg").innerText = "注册中……";
     $.ajax({
            url: 'http://172.16.2.207:8080/face/add',
            cache:false,
            type: 'POST',
            data: {
    base : data,
    user_id : user_id
      },
            dataType: 'json',
            success : function(rs){
    document.getElementById("msg").innerText = rs.msg;
            },
            error: function(e){
              console.log(e);
            }
        });
  }
  //请求摄像头权限
  navigator.mediaDevices.getUserMedia(videoObj)
  .then(function(stream){
    //成功回调函数,把流给video标签
    video.srcObject = stream;
    video.play();
  })
  .catch(errBack);
}, false);
登录页面login
页面:
<!DOCTYPE html>
<html>
  <head>
  <meta charset="UTF-8">
  <title></title>
  </head>
  <body>
  <video id="video" src="" autoplay="autoplay" loop="loop" muted="muted"></video>
  <canvas id="canvas" style="border:1px solid #d3d3d3;"></canvas>
  <div id="support"></div>
  <div id="msg" style="font-size: 50px;"></div>
  </body>
  <script type="text/javascript" src="js/login.js" ></script>
  <script src="js/jquery-1.8.2.min.js"></script>
</html>
JS:
//判断浏览器是否支持HTML5 Canvas
window.onload = function() {
  try {
  //动态创建一个canvas元 ,并获取他2Dcontext。如果出现异常则表示不支持 
  document.createElement("canvas").getContext("2d");
  } catch(e) {
  document.getElementByIdx("support").innerHTML = "浏览器不支持HTML5 CANVAS";
  }
}
//这段代 主要是获取摄像头的视频流并显示在Video 签中
window.addEventListener("DOMContentLoaded", function() {
  var canvas = document.getElementById("canvas"),
  context = canvas.getContext("2d"),
  video = document.getElementById("video"),
  videoObj = {
    "video": true
  },
  errBack = function(error) {
    console.log("Video capture error: ", error.code);
    alert("不支持");
  };
  canvas.width = 450;
  canvas.height = 600;
  //请求摄像头权限
  this.navigator.mediaDevices.getUserMedia(videoObj)
  .then(function(stream){
  video.srcObject = stream;
  video.play();
  //拍照按钮,每0.5秒canvas从video中画到画布,转成base64发给后台
  var login = setInterval(function() {
    context.drawImage(video, 0, 0, 450, 600);
    // 把画布的内容转换为base64编码格式的图片
      var data = canvas.toDataURL( 'image/png', 1 );  //1表示质量(无损压缩)
    $.ajax({
             url: 'http://172.16.2.207:8080/face/login',
             cache:false,
             type: 'POST',
             data: {
      base: data
        },
             dataType: 'json',
             success : function(rs){
              if(rs!=null){
                document.getElementById("msg").innerText = "用户"+rs.user_id+"登录成功";
                //隐藏控件
                $("#video").hide();
        $("#canvas").hide();
      //停止定时发送
                clearInterval(login);
              }
             },
             error: function(e){
             }
         });
  }, 500);
  })
  .catch(errBack);
}, false);

前端就是这些了,注释也加的很详细了。


后台代码码云地址:face: 虹软人脸识别


如果本文帮助到了你,别忘了点赞加关注哦


你点的每个赞,我都认真当成了喜欢


推荐阅读:做了个springboot接口参数解密的工具,我给它命名为万能钥匙(已上传maven中央仓库,附详细使用说明)


相关文章
|
4月前
|
存储 JavaScript API
百宝箱开放平台 ✖️ Node.js SDK
开发者可以通过安装 Node.js SDK 的方式将百宝箱的 OpenAPI 集成到自有系统中,从而在外部系统中发起智能体对话。
259 0
百宝箱开放平台 ✖️ Node.js SDK
|
4月前
|
Java API 开发工具
百宝箱开放平台 ✖️ Java SDK
百宝箱提供Java SDK,支持开发者集成其开放能力。需先发布应用,准备Java 8+及Maven环境,通过添加依赖安装SDK,并初始化客户端调用对话型或生成型智能体,实现会话管理、消息查询与文件上传等功能。
1377 0
百宝箱开放平台 ✖️ Java SDK
|
前端开发 安全 开发工具
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
774 90
【11】flutter进行了聊天页面的开发-增加了即时通讯聊天的整体页面和组件-切换-朋友-陌生人-vip开通详细页面-即时通讯sdk准备-直播sdk准备-即时通讯有无UI集成的区别介绍-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
8月前
|
存储 Java API
MinIO Java SDK 7.1.4 升级到 8.5.17 需要注意什么
现在我需要你帮我分析对比这个两个sdk在对外的接口设计上是否有不兼容的变更
646 5
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
811 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
人工智能 JavaScript 关系型数据库
【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
460 14
【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
|
前端开发 JavaScript Java
【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
591 13
【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
|
12月前
|
SQL JavaScript 安全
【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
523 11
【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
|
人工智能 JavaScript 安全
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
593 13
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
|
12月前
|
存储 缓存 Java
java语言后台管理ruoyi后台管理框架-登录提示“无效的会话,或者会话已过期,请重新登录。”-扩展知识数据库中密码加密的方法-问题如何解决-以及如何重置若依后台管理框架admin密码-优雅草卓伊凡
java语言后台管理ruoyi后台管理框架-登录提示“无效的会话,或者会话已过期,请重新登录。”-扩展知识数据库中密码加密的方法-问题如何解决-以及如何重置若依后台管理框架admin密码-优雅草卓伊凡
1708 3
java语言后台管理ruoyi后台管理框架-登录提示“无效的会话,或者会话已过期,请重新登录。”-扩展知识数据库中密码加密的方法-问题如何解决-以及如何重置若依后台管理框架admin密码-优雅草卓伊凡

热门文章

最新文章

  • 1
    前端如何存储数据:Cookie、LocalStorage 与 SessionStorage 全面解析
    770
  • 2
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(九):强势分析Animation动画各类参数;从播放时间、播放方式、播放次数、播放方向、播放状态等多个方面,完全了解CSS3 Animation
    348
  • 3
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(八):学习transition过渡属性;本文学习property模拟、duration过渡时间指定、delay时间延迟 等多个参数
    272
  • 4
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(七):学习ransform属性;本文学习 rotate旋转、scale缩放、skew扭曲、tanslate移动、matrix矩阵 多个参数
    237
  • 5
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(六):全方面分析css的Flex布局,从纵、横两个坐标开始进行居中、两端等元素分布模式;刨析元素间隔、排序模式等
    343
  • 6
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(五):背景属性;float浮动和position定位;详细分析相对、绝对、固定三种定位方式;使用浮动并清除浮动副作用
    477
  • 7
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(四):元素盒子模型;详细分析边框属性、盒子外边距
    279
  • 8
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(三):元素继承关系、层叠样式规则、字体属性、文本属性;针对字体和文本作样式修改
    160
  • 9
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(二):CSS伪类:UI伪类、结构化伪类;通过伪类获得子元素的第n个元素;创建一个伪元素展示在页面中;获得最后一个元素;处理聚焦元素的样式
    273
  • 10
    【CSS】前端三大件之一,如何学好?从基本用法开始吧!(一):CSS发展史;CSS样式表的引入;CSS选择器使用,附带案例介绍
    309