微信小程序结合Java后端实现登录注册

微信小程序登录

    • 微信用户,授权信息,相关实体类
      • 微信用户表
      • 实体类
      • 获取微信的token
      • 常量抽取
    • httpClient工具,用于发送请求获取token
      • nbplus pom
      • 工具类
    • 请求微信后台,获取openid,返回的是json
    • 登录或者注册完整业务实现,登录成功返回jwt签名
      • cotroller实现
      • 业务实现
    • 设置拦截器对要访问的方法拦截,并判断jwt token
      • 增加注解,灵活判断哪些controlelr方法需要配token验证
      • 拦截器

登录,微信api
后台获取微信token,包括openid在内

登录流程
1. 小程序发起wx.login请求获取jscode
2. 获取jscode,发送到自己的后端
3. 自己的后台发请求带上jscode, appid(当前用户的唯一标识), secretkey到微信的后端获取session_key, openid
4. 返回session_key或者openid到页面
5. 页面再发请求到后台验证openid 是否存在, 存在即登录。
不存在 保存openid到个人数据库,即注册。
6. 登录成功后,返回jwt token签名
7. 页面通过微信app.js 的globalData保存openid,jwt token信息

微信用户,授权信息,相关实体类

微信用户表

create table forum_info.wei_xin_user
(id  int auto_increment  primary key,user_name   varchar(30) null,user_pwd    varchar(30) null,# 这里的密码没用,登录只是验证open_id, # 所以说小程序登录是不需要密码的额。它问你是否授权。# 然后收集你的其它个人信息。create_date date        null,wx_open_id  varchar(30) null
);

实体类

package top.bitqian.entity;import java.util.Date;
import java.io.Serializable;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;/*** <p>*  微信用户注册实体类~* </p>** @author echo lovely* @since 2020-12-04*/
@Data
@EqualsAndHashCode(callSuper = false)
public class WeiXinUser implements Serializable {private static final long serialVersionUID = 1L;@TableId(type = IdType.AUTO)private Integer id;private String userName;private String userPwd;private Date createDate;private String wxOpenId;}

获取微信的token

文档里面的字段搬过来。

package top.bitqian.entity;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** <p>*     调用微信接口 返回授权信息~* </p>* @author echo lovely* @date 2020/12/4 21:56*/@Data
@NoArgsConstructor
@AllArgsConstructor
public class Code2Session {//用户唯一标识private String openid;// 会话密钥private String session_key;// 用户在开放平台的唯一标识符private String unionid;// 错误码private Integer errcode;// 错误信息private String errmsg;}

常量抽取

package top.bitqian.config;/*** @author echo lovely* @date 2020/12/4 22:03*/public class WeiXinPostParamConstant {/*** 小程序appid*/public static String APP_ID = "小程序开发者官网生成";/*** 小程序 appSecret*/public static String SECRET = "小程序开发者官网生成";/*** 登录时获取的 code*/public static String JS_CODE = "";/*** 授权类型,此处只需填写 authorization_code* grant_type ctrl + shit + u*/public static String GRANT_TYPE = "";}

httpClient工具,用于发送请求获取token

nbplus pom

这里是boot,省略了version

<!-- http工具类 --><!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient --><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><!--<version>4.5.6</version>--></dependency><!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpcore --><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpcore</artifactId><!--<version>4.4.10</version>--></dependency>

工具类

package top.bitqian.config;import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Map;import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;/*** HTTPClient 工具类.* @author echo lovely* @date 2020/12/4 21:44*/@Configuration
@SuppressWarnings(value = "all")
public class HttpClientUtil {private static final Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);private static PoolingHttpClientConnectionManager connectionManager;private static String DEFAULT_STR = "";private static String UTF_8 = "UTF-8";private final static int CONNECT_TIMEOUT = 3000;// 连接超时毫秒 ps:表示建立连接的超时时间private final static int SOCKET_TIMEOUT = 10000;// 传输超时毫秒 ps:表示数据传输处理时间private final static int REQUESTCONNECT_TIMEOUT = 2000;// 从线程池获取连接超时时间毫秒private final static int MAX_TOTAL = 50;// 线程池的最大连接数private final static int CONNECT_DEFAULT_ROUTE = 5;// 每个路由默认基础的连接数private static void init() {if (connectionManager == null) {connectionManager = new PoolingHttpClientConnectionManager();connectionManager.setMaxTotal(MAX_TOTAL);// 整个连接池最大连接数// 可用空闲连接过期时间,重用空闲连接时会先检查是否空闲时间超过这个时间,如果超过,释放socket重新建立//connectionManager.setValidateAfterInactivity(50000);connectionManager.setDefaultMaxPerRoute(CONNECT_DEFAULT_ROUTE);// 每路由最大连接数,默认值是2}}/*** 通过连接池获取HttpClient** @return*/private static CloseableHttpClient getHttpClient() {init();Builder builder = RequestConfig.custom();RequestConfig config = builder.setSocketTimeout(SOCKET_TIMEOUT).setConnectTimeout(CONNECT_TIMEOUT).setConnectionRequestTimeout(REQUESTCONNECT_TIMEOUT).build();CloseableHttpClient client = HttpClients.custom().setMaxConnPerRoute(CONNECT_DEFAULT_ROUTE).disableConnectionState().setDefaultRequestConfig(config).setConnectionManager(connectionManager).build();return client;}/*** @param url* @return* @throws IOException*/public static String httpGetRequest(String url) throws Exception {HttpGet httpGet = new HttpGet(url);return getResult(httpGet);}public static String httpGetRequest(String url, Map<String, Object> params) throws Exception {URIBuilder ub = new URIBuilder();ub.setPath(url);ArrayList<NameValuePair> pairs = covertParams2NVPS(params);ub.setParameters(pairs);HttpGet httpGet = new HttpGet(ub.build());return getResult(httpGet);}public static String httpGetRequest(String url, Map<String, Object> headers, Map<String, Object> params) throws Exception {URIBuilder ub = new URIBuilder();ub.setPath(url);ArrayList<NameValuePair> pairs = covertParams2NVPS(params);ub.setParameters(pairs);HttpGet httpGet = new HttpGet(ub.build());for (Map.Entry<String, Object> param : headers.entrySet()) {httpGet.addHeader(param.getKey(), String.valueOf(param.getValue()));}return getResult(httpGet);}public static String httpPostRequest(String url) throws IOException {HttpPost httpPost = new HttpPost(url);return getResult(httpPost);}public static String httpPostRequest(String url, Map<String, Object> params) throws Exception {HttpPost httpPost = new HttpPost(url);ArrayList<NameValuePair> pairs = covertParams2NVPS(params);httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF_8));return getResult(httpPost);}public static String httpPostRequest(String url, String jsonParams) throws Exception {HttpPost httpPost = new HttpPost(url);StringEntity se = new StringEntity(jsonParams, UTF_8);httpPost.setEntity(se);httpPost.setHeader("Content-Type", "application/json");return getResult(httpPost);}public static String httpPostXMLDataRequest(String url, String xmlData) throws Exception {HttpPost httpPost = new HttpPost(url);httpPost.addHeader("Content-Type", "text/xml");httpPost.setEntity(new StringEntity(xmlData, UTF_8));return getResult(httpPost);}/*** post** @param url     (a=3&b=2 形式)* @param headers 请求头* @param params  参数* @return* @throws IOException*/public static String httpPostRequest(String url, Map<String, Object> headers, Map<String, Object> params) throws Exception {HttpPost httpPost = new HttpPost(url);for (Map.Entry<String, Object> param : headers.entrySet()) {httpPost.addHeader(param.getKey(), String.valueOf(param.getValue()));}ArrayList<NameValuePair> pairs = covertParams2NVPS(params);httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF_8));return getResult(httpPost);}private static ArrayList<NameValuePair> covertParams2NVPS(Map<String, Object> params) {ArrayList<NameValuePair> pairs = new ArrayList<NameValuePair>();for (Map.Entry<String, Object> param : params.entrySet()) {pairs.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue())));}return pairs;}/*** post** @param url     (JSON 形式)* @param headers 请求头* @param params  参数* @return* @throws IOException*/public static String httpPostRequest(String url, Map<String, Object> headers, String jsonParams) throws Exception {HttpPost httpPost = new HttpPost(url);for (Map.Entry<String, Object> param : headers.entrySet()) {httpPost.setHeader(param.getKey(), String.valueOf(param.getValue()));}StringEntity se = new StringEntity(jsonParams, UTF_8);httpPost.setEntity(se);return getResult(httpPost);}/*** 处理Http请求** @param request* @return string* @throws IOException*/private static String getResult(HttpRequestBase request) throws IOException {CloseableHttpClient httpClient = getHttpClient();CloseableHttpResponse response = null;InputStream in = null;try {response = httpClient.execute(request);HttpEntity entity = response.getEntity();in = response.getEntity().getContent();if (entity != null) {String result = EntityUtils.toString(entity, Charset.forName(UTF_8));response.close();return result;}} catch (ConnectTimeoutException e) {// 连接超时异常logger.error("connect timeout {}", e);} catch (SocketTimeoutException e) {// 读取超时异常logger.error("read timeout {}", e);} catch (ClientProtocolException e) {// 该异常通常是协议错误导致:比如构造HttpGet对象时传入协议不对(将'http'写成'htp')or响应内容不符合logger.error("protocol exception {}", e);} catch (ParseException e) {// 解析异常logger.error("parse exception {}", e);} catch (IOException e) {// 该异常通常是网络原因引起的,如HTTP服务器未启动等logger.error("network exception {}", e);} catch (Exception e) {logger.error("other exception {}", e);} finally {if (response != null) {try {response.close();} catch (IOException e) {e.printStackTrace();}}//in.close();作用就是将用完的连接释放,下次请求可以复用//这里特别注意的是,如果不使用in.close();而仅仅使用response.close();结果就是连接会被关闭,并且不能被复用,这样就失去了采用连接池的意义。if (in != null) {try {in.close();} catch (IOException e) {e.printStackTrace();}}}return DEFAULT_STR;}public static void main(String[] args) {String str = null;try {str = HttpClientUtil.httpGetRequest("https://www.baidu.com");} catch (Exception e) {e.printStackTrace();}System.out.println(str);}
}

请求微信后台,获取openid,返回的是json

json转实体类用

  <dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency>

   /*** 获取openid session_key* @param jsCode 小程序请求到的jsCode* @return 授权信息~*/@Overridepublic Code2Session getWinXinJson(String jsCode) {// https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_codeStringBuilder url = new StringBuilder();url.append("https://api.weixin.qq.com/sns/jscode2session?appid=");url.append(WeiXinPostParamConstant.APP_ID);url.append("&secret=");url.append(WeiXinPostParamConstant.SECRET);url.append("&js_code=");url.append(jsCode);url.append("&grant_type=authorization_code");try {String weiXinJson = HttpClientUtil.httpGetRequest(url.toString());System.out.println(weiXinJson);return new ObjectMapper().readValue(weiXinJson, Code2Session.class);} catch (Exception e) {e.printStackTrace();}return null;}

登录或者注册完整业务实现,登录成功返回jwt签名

我的java json web token签名实现,工具类

cotroller实现

package top.bitqian.controller;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import top.bitqian.config.AuthTokenCheck;
import top.bitqian.entity.Code2Session;
import top.bitqian.entity.WeiXinUser;
import top.bitqian.service.WeiXinUserService;import javax.servlet.http.HttpServletRequest;
import java.util.Date;/*** 微信登录 controller~* @author echo lovely* @date 2020/12/4 21:36*/@RestController
@Slf4j
public class WeiXinUserController {@Autowiredprivate WeiXinUserService weiXinUserService;// 调用微信的接口获取 app_id@RequestMapping("/getCode/{jsCode}")public Code2Session getWinXinJson(@PathVariable("jsCode") String jsCode) {return weiXinUserService.getWinXinJson(jsCode);}// 用户提交wx_id 过来注册@RequestMapping("/wx_user/register")public boolean doRegister(WeiXinUser user) {// 账号存在..WeiXinUser tmpUser = weiXinUserService.getWeiXinUserByOpenId(user.getWxOpenId());if (tmpUser != null) {return false;}// 不存在,即注册user.setCreateDate(new Date(System.currentTimeMillis()));weiXinUserService.addWeiXinUser(user);return true;}@RequestMapping("/wx_user/login")public String doLogin(WeiXinUser weiXinUser) throws Exception {return weiXinUserService.doLogin(weiXinUser);}@AuthTokenCheck@RequestMapping("/wx_user/msg")public String testJwtToken(HttpServletRequest request) {Object userId = request.getAttribute("id");System.out.println("userId from page token~=======>" + userId);return "auth by token~" + userId;}}

业务实现

package top.bitqian.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import top.bitqian.config.HttpClientUtil;
import top.bitqian.config.JwtUtil;
import top.bitqian.config.WeiXinPostParamConstant;
import top.bitqian.entity.Code2Session;
import top.bitqian.entity.WeiXinUser;
import top.bitqian.mapper.WeiXinUserMapper;
import top.bitqian.service.WeiXinUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;/*** <p>*  服务实现类* </p>** @author echo lovely* @since 2020-12-04*/
@Service
public class WeiXinUserServiceImpl extends ServiceImpl<WeiXinUserMapper, WeiXinUser> implements WeiXinUserService {/*** 获取openid session_key* @param jsCode 小程序请求到的jsCode* @return 授权信息~*/@Overridepublic Code2Session getWinXinJson(String jsCode) {// https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_codeStringBuilder url = new StringBuilder();url.append("https://api.weixin.qq.com/sns/jscode2session?appid=");url.append(WeiXinPostParamConstant.APP_ID);url.append("&secret=");url.append(WeiXinPostParamConstant.SECRET);url.append("&js_code=");url.append(jsCode);url.append("&grant_type=authorization_code");try {String weiXinJson = HttpClientUtil.httpGetRequest(url.toString());System.out.println(weiXinJson);return new ObjectMapper().readValue(weiXinJson, Code2Session.class);} catch (Exception e) {e.printStackTrace();}return null;}@Autowiredprivate WeiXinUserMapper userMapper;public WeiXinUser getWeiXinUserByOpenId(String openId) {WeiXinUser tmpUser = new WeiXinUser();tmpUser.setWxOpenId(openId);QueryWrapper<WeiXinUser> queryWrapper = new QueryWrapper<>(tmpUser);return userMapper.selectOne(queryWrapper);}@Overridepublic String doLogin(WeiXinUser user) throws Exception {// 登录需要:// 1. 根据小程序传来的openid验证数据库中的id,看是否存在~WeiXinUser weiXInUser = getWeiXinUserByOpenId(user.getWxOpenId());System.out.println("doLogin----->" + weiXInUser);if (weiXInUser != null) {// 2. 存在 返回jwt签名~  页面保存return JwtUtil.createToken(weiXInUser);}// 2. 不存在 return ""return null;}@Overridepublic void addWeiXinUser(WeiXinUser user) {//  userMapper.addWeiXinUser(user);userMapper.insert(user);}
}

设置拦截器对要访问的方法拦截,并判断jwt token

增加注解,灵活判断哪些controlelr方法需要配token验证

package top.bitqian.config;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 要给哪些方法进行token验证*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AuthTokenCheck {}

拦截器

package top.bitqian.config;import com.auth0.jwt.interfaces.Claim;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Map;/*** 拦截器* @author echo lovely* @date 2020/12/5 20:28*/@Configuration
public class InterceptorConfig implements WebMvcConfigurer {/*** 拦截器需要被注册进..* @param registry*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new TokenInterceptor());}}class TokenInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception {System.out.println("进来l...");// 对方法进行拦截if (! (handler instanceof HandlerMethod)) {return true;}// 方法对象HandlerMethod handlerMethod = (HandlerMethod) handler;Method methodTarget = handlerMethod.getMethod();// 对加了AuthTokenCheck注解 的方法进行token验证~boolean isTokenTarget = methodTarget.isAnnotationPresent(AuthTokenCheck.class);if (isTokenTarget) {// 进行token验证, 头部里面的token// String authorizeToken = request.getHeader("authorize_token");String authorizeToken = request.getParameter("authorize_token");try {Map<String, Claim> claimMap = JwtUtil.parseToken(authorizeToken);// 解析获取token中的用户id~ 也可根据相应的键获取其它信息Integer id = claimMap.get("id").asInt();String userName = claimMap.get("userName").asString();System.out.println(id + "\t" + userName);// 放入request中request.setAttribute("id", id);return true;} catch (Exception e) {return false;}}return true;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/420459.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

consul作为服务注册中心

consulconsul下载地址providerorderconsul 由go语言编写的一款优秀的服务注册中心应用。 https://www.consul.io/intro/index.html 中文文档 功能&#xff1a; 1. 服务发现 提供HTTP和DNS两种发现方式 2. 健康监测 支持多种协议&#xff0c;HTTP、TCP、Docker、Shell脚本定制…

ribbon, restTemplate 负载均衡服务调用

ribbonribbon conceptribbon核心组件IRule模仿源码重写轮询ribbon concept spring cloud ribbon 是基于Netflix ribbon实现的一套客户端负载均衡的工具。 简单的说&#xff0c; Ribbon是Netflix发布的开源项目&#xff0c; 主要功能是提供客户端的软件负载均衡算法和服务 完善的…

ABAP 弹出框自建内容POPUP

可以自己设计内容&#xff0c;仅供参考。转载于:https://www.cnblogs.com/matinal/p/4135269.html

前端学习(1287):node.js的组成

var first hello nodejs; console.log(first);function fn() {console.log("fn函数被调用了"); }fn();for (var i 0; i < 5; i) {console.log(i); }if (true) {console.log(123); } 运行结果

gateway路由网关,zuul的替代品

zuul1 low, zuul2 还没长大呢&#xff0c;不敢用。 gateway 基于netty, spring flux, reactor 异步非阻塞, 快呀。 与spring良好整合, spring社区的呢。官方推荐。https://spring.io/projects/spring-cloud-gateway https://cloud.spring.io/spring-cloud-static/spring-cloud-…

ASP.NET MVC 5 入门教程 (3) 路由route

文章来源&#xff1a; Slark.NET-博客园 http://www.cnblogs.com/slark/p/mvc-5-get-started-route.html 上一节&#xff1a;ASP.NET MVC 5 入门教程 (2) 控制器Controller 下一节&#xff1a;ASP.NET MVC 5 入门教程 (4) View和ViewBag 源码下载&#xff1a;点我下载 上一节我…

spring cloud config 配置中心

/(ㄒoㄒ)/~~ 还有好多要学鴨 cloud config分布式面临的系统配置问题&#xff0c;如何解决分为服务端配置与客户端配置客户端不能自动更新git上的配置分布式面临的系统配置问题&#xff0c;如何解决 1. 分布式系统面临的配置问题 微服务意味着要将单体应用中的业务拆分成一个个…

前端学习(1289):nodejs模块化的开发规范

demo02.js const add (n1, n2) > n1 n2exports.add add; demo03.js const a require(./demo02.js); console.log(a); 运行结果

spring cloud bus消息总线

解决的痛点: 当git仓库的配置更新后&#xff0c; cloud config 客户端不能获取到配置信息的问题, 需要手动发送请求&#xff0c;刷新配置。 可以参照 spring cloud config cloud busbus消息总线使用rabbitMQ推送消息原理架构实现使用curl命令刷新客户端的配置bus bus配合conf…

前端学习(1290):nodejs模块化的开发导出另一种方式

demo04.js const greeting _name > hello ${_name};module.exports.greeting greeting; demo05.js const a require(./demo04.js); console.log(a); console.log(a.greeting(geyao)); 运行结果 demo04.js const greeting _name > hello ${_name}; const x 100;…

SpringCloud Stream消息驱动

为啥有这个技术&#xff1f;&#xff1f;&#xff1f; 1. 这个stream是操作消息队列的&#xff0c;简化&#xff0c;学习消息队列的成本降低。 2. 可操作rabbitMQ兔子message queue&#xff0c;kafaka&#xff0c;可理解为jdbc可操作oracle, mysql.. 3. spring家的技术学就完了…

前端学习(1291):nodejs的系统模块文件读取操作

//通过模块对模块进行引入 const fs require(fs); //读取文件 fs.readFile(./demo01.js, utf8, (err, doc) > {console.log(err);console.log(doc); }) 运行结果

解决MySQL忘记root密码

网上有很多关于忘记MySQL root密码的一些文章&#xff0c;里面都有写怎么去解决&#xff0c;但有时觉得写得太恶心&#xff0c;要么一字不漏的抄别人的&#xff0c;要么就说得不清不楚&#xff0c;好了&#xff0c;不吐槽了&#xff0c;以下是解决的整个过程。 首先我们要知道忘…

前端学习(1292):文件写入操作

const fs require(fs);fs.writeFile(./demo.txt, 即将要写入的内容, err > {if (err ! null) {console.log(err);return;}console.log(文件内容写入成功); }) 运行结果

前端学习(1293):系统模块path路径操作

//导入path模块 const path require(path); //路径拼接 const finaPath path.join(public, uploads, avater); console.log(finaPath); 运行结果

前端学习(1294):相对路径和绝对路径

const fs require(fs); const path require(path); console.log(__dirname); console.log(path.join(__dirname, ./demo01.js)); fs.readFile(path.join(__dirname, ./demo01.js), utf8, (err, doc) > {console.log(err);console.log(doc); }) 运行结果