首页
文章分类
逆向网安
中英演讲
杂类教程
学习笔记
前端开发
汇编
数据库
.NET
服务器
Python
Java
PHP
Git
算法
安卓开发
生活记录
读书笔记
作品发布
人体健康
网上邻居
留言板
欣赏小姐姐
关于我
Search
登录
1
利用AList搭建家庭个人影音库
4,660 阅读
2
浅尝Restful Fast Request插件,一句话完成 逆向过程
3,963 阅读
3
完美破解The Economist付费墙
2,729 阅读
4
i茅台app接口自动化csharp wpf实现,挂机windows服务器每日自动预约
2,610 阅读
5
青龙面板基本使用并添加修改微信/支付宝步数脚本
2,035 阅读
Search
标签搜索
PHP
Laravel
前端
csharp
安卓逆向
JavaScript
Python
Java
爬虫
抓包
Git
winform
android
Fiddler
Vue
selenium
LeetCode
每日一题
简单题
docker
Hygge
累计撰写
95
篇文章
累计收到
445
条评论
首页
栏目
逆向网安
中英演讲
杂类教程
学习笔记
前端开发
汇编
数据库
.NET
服务器
Python
Java
PHP
Git
算法
安卓开发
生活记录
读书笔记
作品发布
人体健康
页面
网上邻居
留言板
欣赏小姐姐
关于我
用户登录
搜索到
95
篇与
的结果
2022-09-12
Git分支命名规范
Git分支命名规范一、git分支命名规范git分支分为集成分支,功能分支、和修复分支。分别命名为develop,feature和hotfix,均为单数。不可使用features、future、hotfixes、hotfixs 等错误名称。分支名描述master / main主分支,永远是可用的稳定版本,不能直接在该分支上开发develop开发主分支,所有新功能以这个分支来创建自己的开发分支,该分支只做合并操作,不能直接在该分支上进行开发feature-xxx功能开发分支,在develop上创建分支,以自己开发功能模块命名,功能测试正常后合并到develop分支feature-xxx-fix功能bug修复分支,feature分支合并之后发现bug,在develop上创建分支进行修复,之后合并回develop分支hotfix-xxx紧急bug修改分支,在master分支上创建,修复完成后合并到masterbugfix短期从develop创建release短期从develop创建PS:1.feature分支在申请合并之后,未合并之前还是可以提交代码的,所以feature在合并之前还可以在原分支上继续修复bug2.一个分支尽量开发一个功能模块。不要多个功能模块在一个分支上开发3.feature分支在申请合并之前,最好是先pull一下主分支develop,看一下有没有冲突,如果有,先解决冲突后再申请合并二、Branch功能详解master 负责记录上线版本的迭代,该分支代码与线上代码是完全一致的主分支。develop负责记录相对稳定的版本,所有的feature分支和bugfix分支都从该分支创建开发分支feature用于开发新的功能,不同的功能创建不同的功能分支,功能分支开发完成后并自测,自测通过之后,需要合并到develop分支,之后删除该分支。bugfix用于修复不紧急的bug,普通bug均需要创建bugfix分支开发,开发完成自测,pass之后合并到develop分支后删除该分支release用于代码上线准备,该分支从develop分支创建,创建之后由测试人员发布到测试环境进行测试,测试过程中发现bug需要开发人员在该release分支上进行bug修复,所有bug修复完成后,在上线之前,需要合并该release分支到master分支和develop分支。hotfix该分支只有在紧急情况下使用,从master分支创建,用于紧急修复线上bug,修复完成后,需要合并该分支到master分支以便上线,同时需要再合并到develop分支紧急bug修复分支。三、Branch命名规范分支名格式例子功能分支feature/功能名称feature/loginbug修复分支bugfix/bug名称bugfix/add-user紧急bug修复分支hotfix/bug名称hotfix/delete预发布分支release/预发布版本名称release/add-user四、分支用途详解我们刚刚熟悉了git中常用的分支,那么这些分支有什么意义呢?这么说吧,如果你是一个人开发,那么这确实没什么用,当你在一个团队时就发挥了很大的作用。一般作用下,master分支和线上版本是保持一致的,那么我们需要对它非常重视。一切开发任务都不能在这里进行。因为在开发过程中如果出现bug就会弄脏master分支。如果我们在develop分支上开发,不管出什么错误都不怕,因为master是干净的,实在不行可以从master重新拉取没有问题的项目。这个就是分支其中一个作用。现在是这样的情况:我们在develop分支上完成了项目,那么之后对各个分支是怎么处理呢?过程大概是这样的:将我们的develop分支合并到release分支,这个是一个预发布分支,这个预发布分支是交给测试的人员。测试人员在release分支上拉取完整项目进行测试,在测试过程中发现了一个bug。测试人员找到了开发人员,开发人员在release分支上修改好问题,所有问题都解决了,这时release分支合并到master分支和develop分支。这时开发人员的develop是最新的,master分支也是最新的。另外一种情况是这样的:线上产品使用过程中突然出现了一个bug,这时非常紧急的情况,这时需要处理的步骤大致如下:创建一个紧急bug分支名为hotfix,将master分支拉取到hotfix分支。紧急修改完bug之后将hotfix同步到master分支和develop分支,再删除hotfix分支。世界就恢复平静了。总之,分支会让你在更安全的环境下开发,git里面什么后悔药都有的。引用1.Git分支命名规范 : [https://blog.51cto.com/u_15057832/3527964](
2022年09月12日
218 阅读
0 评论
0 点赞
2022-09-12
Git代码Commit规范
<type>(<scope>): <subject>规范git commit到底有哪些好处呢?便于程序员对提交历史进行追溯,了解发生了什么情况。一旦约束了commit message,意味着我们将慎重的进行每一次提交,不能再一股脑的把各种各样的改动都放在一个git commit里面,这样一来整个代码改动的历史也将更加清晰。格式化的commit message才可以用于自动化输出Change log。type(必须)用于说明git commit的类别,只允许使用下面的标识。typedescfeat新功能(feature)fix/to修复bug,可以是QA发现的BUG,也可以是研发自己发现的BUG。fix:产生diff并自动修复此问题。适合于一次提交直接修复问题。to:只产生diff不自动修复此问题。适合于多次提交。最终修复问题提交时使用fixdocs文档(documentation)style格式(不影响代码运行的变动)refactor重构(即不是新增功能,也不是修改bug的代码变动)。perf优化相关,比如提升性能、体验。test增加测试。chore构建过程或辅助工具的变动。revert回滚到上一个版本。merge代码合并。sync同步主线或分支的Bug。scope(可选)scope用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。例如在Angular,可以是location,browser,compile,compile,rootScope, ngHref,ngClick,ngView等。如果你的修改影响了不止一个scope,你可以使用*代替。subject(必须)subject是commit目的的简短描述,不超过50个字符。建议使用中文(感觉中国人用中文描述问题能更清楚一些)。结尾不加句号或其他标点符号。根据以上规范git commit message将是如下的格式:fix(DAO):用户查询缺少username属性 feat(Controller):用户查询接口开发
2022年09月12日
183 阅读
0 评论
0 点赞
2022-09-10
通过Fiddler抓包调试PHP内Guzzle网络请求
场景最近在做设计素材网解析下载,后台框架使用Laravel网络请求框架使用HTTP ClientLaravel provides an expressive, minimal API around the Guzzle HTTP client, allowing you to quickly make outgoing HTTP requests to communicate with other web applications. Laravel's wrapper around Guzzle is focused on its most common use cases and a wonderful developer experience.一、Fiddler配置HTTP 抓包Fiddler 主菜单 -> Tools -> Fiddler Options-> Connections-> 选中 Allowremote computers to connect装有 fiddler 的机器,找出能远程访问的 IP,一般局域网内也就是本机 IP。被抓包调试的设备在网络代理那里启用代理 -> 代理 IP 就是上面说的 IP-> 端口号默认为 8888 (可以在 fiddler 中 Connections 标签页修改)这样就 OK 了。HTTPS 抓包Fiddler 主菜单 -> Tool->Fiddler Options->HTTPS -> 选中 decrypt https traffic 和 ignore server certificate errors会提示你安装证书,必要要安装。然后同 HTTP 抓包一样操作二、代码配置代理$response = Http::withCookies(cookieStrToArray($cookie->content), 'nipic.cn') ->withOptions( [ 'proxy' => '127.0.0.1:8888', // 端口为Fiddler中配置的端口 'verify' => false, // 禁用证书验证 ]) ->withHeaders([ 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 'Accept-Encoding' => 'gzip, deflate, br', ]) ->withUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36') ->get("https://down.nipic.cn/download?id=$resourceId")->body();再次请求可以看到Fiddler拦截到了请求。三、使用Telescope需要安装一下对应的依赖# You may use the Composer package manager to install Telescope into your Laravel project: composer require laravel/telescope --dev # After installing Telescope, publish its assets using the telescope:install Artisan command. After installing Telescope, you should also run the migrate command in order to create the tables needed to store Telescope's data: php artisan telescope:install php artisan migrate只有使用HTTP Client才会被记录,而且请求和响应的记录信息不太全,所以使用Fiddler还是更好的选择。引用1.Fiddler 抓包调试 : https://www.chengxiaobai.cn/skills/fiddler-capture-debugging.html2.laravel中使用Guzzle 报 unable to get local issuer certificate错误信息:https://blog.csdn.net/worldmakewayfordream/article/details/1143020203.Guzzle 6 请求选项 :https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html4.如何获取php向其它网站发起了什么请求,有什么办法?: https://learnku.com/laravel/t/673455.HTTP Client : https://laravel.com/docs/9.x/http-client6.Laravel Telescope : https://laravel.com/docs/9.x/telescope
2022年09月10日
466 阅读
0 评论
0 点赞
2022-09-06
Laravel Breeze(vue) and Homestead - npm run dev and HMR not working
Laravel Breeze(vue) and Homestead - npm run dev and HMR not workingQuestionI followed the instructions from the documentation:Homestead: https://laravel.com/docs/9.x/homestead#installation-and-setupBreeze with Vue and inertia: https://laravel.com/docs/9.x/starter-kits#breeze-and-inertiaWhen I run npm run build everything works fine. I can visit my new app over http://homestead.test/. When I try to use the dev server with hot reload npm run dev, the debug console in my browser (host) tells:GET http://127.0.0.1:5173/@vite/client net::ERR_CONNECTION_REFUSED GET http://127.0.0.1:5173/resources/js/app.js net::ERR_CONNECTION_REFUSEDI already tried to change my package.json file to from "dev": "vite", to "dev": "vite --host homestead.test", but this only results in the errorsGET http://homestead.test:5173/@vite/client net::ERR_CONNECTION_REFUSED GET http://homestead.test:5173/resources/js/app.js net::ERR_CONNECTION_REFUSEDIn app.blade.php the scripts are imported with @<!DOCTYPE html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title inertia>{{ config('app.name', 'Laravel') }}</title> <!-- Fonts --> <link rel="stylesheet" href="https://fonts.bunny.net/css2?family=Nunito:wght@400;600;700&display=swap"> <!-- Scripts --> @routes @vite('resources/js/app.js') @inertiaHead </head> <body class="font-sans antialiased"> @inertia </body> </html>@routes seems to be a part of the Laravel Ziggy package. No error from this side.But the @vite('resources/js/app.js') and @inertiaHead are throwing errors. These directives link to a wrong destination.How to solve this?AnswerI've found the solution. Add the server part in your vite.config.js. And add the app.css to the inputsimport { defineConfig } from 'vite'; import laravel from 'laravel-vite-plugin'; import vue from '@vitejs/plugin-vue'; export default defineConfig({ server: { hmr: { host: "192.168.56.56", }, host: "192.168.56.56", watch: { usePolling: true, }, }, plugins: [ laravel({ input: ['resources/js/app.js', 'resources/css/app.css'], refresh: true, }), vue({ template: { transformAssetUrls: { base: null, includeAbsolute: false, }, }, }), ], });Quote1.Laravel Breeze (vue) and Homestead - npm run dev and HMR not working : https://stackoverflow.com/questions/73506437/laravel-breeze-vue-and-homestead-npm-run-dev-and-hmr-not-working
2022年09月06日
1,316 阅读
0 评论
0 点赞
2022-08-21
Java-微信公众号实现网站登录功能
序言微信登录常见方式平常大家见到过最多的扫码登录应该是 开放平台网页登录 大概形式就是:点击微信登录后会出现一个黑页面,页面中有一个二维码,扫码后可以自动获取用户信息然后登录,但是这种方式需要申请开放平台比较麻烦。如图利于推广方式另外一种扫码登录方式只需要一个微信服务号就行,大概流程是:点击微信登录,网站自己弹出一个二维码、扫描二维码后弹出公众号的关注界面、只要一关注公众号网站自动登录、第二次扫描登录的时候网站直接登录,这种扫码登录的方式个人觉得非常利于推广公众号。流程如下:一、获取二维码二、前端轮询接口,查看扫码情况未扫描:扫描成功:三、扫描二维码方式一:利于推广方式基本流程使用微信扫码登录 我们肯定要先来了解一下扫码登录的基本流程啦前端首先向服务端发送一个请求,用来获取二维码的url和唯一随机数(用UUID即可,这个随机数可以理解为这个二维码的key值,一一对应,所以尽量要用唯一的)同时服务端要记录这条随机数(存redis和其他数据库均可,找个地方记录下来)前端自从收到二维码和随机数后,展示二维码,并轮询一个检查二维码状态的接口(用来判断用户是否扫码并确认)很关键的一步客户扫码并确认后,会回调给服务端一个请求,服务端就能拿到对应的二维码的key(之前产生的随机数)前端轮询中 发现二维码状态值变为用户扫码已确认的值后,向后端发送业务请求(登录。。。。)前提准备导入依赖 <!-- 对接微信登录 开始 --> <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-mp</artifactId> <version>4.3.8.B</version> </dependency> <!-- 对接微信登录 结束 --> <!-- 读取配置文件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!-- 网络请求框架 --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> <!-- fast json --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.10</version> </dependency> <!-- END fast json -->网络请求工具类util/HttpClientUtil.javapublic class HttpClientUtil { public static String doGet(String url, Map<String, String> param) { // 创建Httpclient对象 CloseableHttpClient httpclient = HttpClients.createDefault(); String resultString = ""; CloseableHttpResponse response = null; try { // 创建uri URIBuilder builder = new URIBuilder(url); if (param != null) { for (String key : param.keySet()) { builder.addParameter(key, param.get(key)); } } URI uri = builder.build(); // 创建http GET请求 HttpGet httpGet = new HttpGet(uri); // 执行请求 response = httpclient.execute(httpGet); // 判断返回状态是否为200 if (response.getStatusLine().getStatusCode() == 200) { resultString = EntityUtils.toString(response.getEntity(), "UTF-8"); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (response != null) { response.close(); } httpclient.close(); } catch (IOException e) { e.printStackTrace(); } } return resultString; } public static String doGet(String url) { return doGet(url, null); } public static String doPost(String url, Map<String, String> param) { // 创建Httpclient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; try { // 创建Http Post请求 HttpPost httpPost = new HttpPost(url); // 创建参数列表 if (param != null) { List<NameValuePair> paramList = new ArrayList<>(); for (String key : param.keySet()) { paramList.add(new BasicNameValuePair(key, param.get(key))); } // 模拟表单 UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList, "utf-8"); httpPost.setEntity(entity); } // 执行http请求 response = httpClient.execute(httpPost); resultString = EntityUtils.toString(response.getEntity(), "utf-8"); } catch (Exception e) { e.printStackTrace(); } finally { try { response.close(); } catch (IOException e) { e.printStackTrace(); } } return resultString; } public static String doPost(String url) { return doPost(url, null); } /** * 请求的参数类型为json * * @param url * @param json * @return {username:"",pass:""} */ public static String doPostJson(String url, String json) { // 创建Httpclient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; try { // 创建Http Post请求 HttpPost httpPost = new HttpPost(url); // 创建请求内容 StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); httpPost.setEntity(entity); // 执行http请求 response = httpClient.execute(httpPost); resultString = EntityUtils.toString(response.getEntity(), "utf-8"); } catch (Exception e) { e.printStackTrace(); } finally { try { response.close(); } catch (IOException e) { e.printStackTrace(); } } return resultString; } }扫码登录工具类util/JsonUtil.javapublic class MyStringUtil { /** * 从字节数组到十六进制字符串转换 */ public static String bytes2HexString(byte[] b) { byte[] buff = new byte[2 * b.length]; for (int i = 0; i < b.length; i++) { buff[2 * i] = hex[(b[i] >> 4) & 0x0f]; buff[2 * i + 1] = hex[b[i] & 0x0f]; } return new String(buff); } private final static byte[] hex = "0123456789ABCDEF".getBytes(); //length用户要求产生字符串的长度 public static String getRandomString(int length) { String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; Random random = new Random(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < length; i++) { int number = random.nextInt(62); sb.append(str.charAt(number)); } return sb.toString(); } }1.公网映射开发的接口需要暴露在公网,微信服务器会进行回调调用。博主开发中配合使用的是: Sunny-Ngrok内网转发内网穿透 - 国内内网映射服务器:https://www.ngrok.cc/ (需要实名认证、支付宝人脸识别、人脸识别费用一两块钱)此时访问http://lisok.free.idcfengye.com/就是访问本地的localhost:80822.微信公众平台测试号地址:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login需要记录appID、appsecret项目中配置application.yml:# 扫描公众号登录 wechat: appId: ... appSecret: ... qrCodeUrl: https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN tokenUrl: https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET # 验签使用的token token: ... # token 随机填配置类:properties/WxConfig/** * @author Hygge * @date 2022/08/13 * @description 微信登录的一些配置信息 通过读取yml获得 */ @Component @Data @ConfigurationProperties(prefix = "wechat") public class WxConfig { private String appId; private String appSecret; private String qrCodeUrl; private String tokenUrl; /** * 验签使用的token */ private String token; }3.配置微信消息路由器MP\_微信消息路由器官方文档:https://github.com/Wechat-Group/WxJava/wiki/MP_%E5%BE%AE%E4%BF%A1%E6%B6%88%E6%81%AF%E8%B7%AF%E7%94%B1%E5%99%A8微信推送给公众号的消息类型很多,而公众号也需要针对用户不同的输入做出不同的反应。如果使用if ... else ...来实现的话非常难以维护,这时可以使用WxMpMessageRouter来对消息进行路由。WxMpMessageRouter支持从4个角度对消息进行匹配,然后交给事先指定的WxMpMessageRouter作用举个栗子,如果没有消息路由,那么所有的消息各种类型都会集中在一块处理,如文本消息、图片消息、订阅通知、扫码通知、取消订阅通知等。有了消息路由就可以将属于一类的消息转发给专门用来处理这类消息的路由(Java类)config/WeChatConfig.java/** * @author Hygge * @date 2022/08/20 * @description */ @Configuration public class WeChatConfig { @Resource private WxConfig wxConfig; @Resource private WxMpService wxMpService; @Resource private TextHandler textHandler; @Resource private ImageHandler imageHandler; @Resource private SubscribeHandler subscribeHandler; @Resource private UnSubscribeHandler unSubscribeHandler; @Resource private ScanHandler scanHandler; // 单例 @Bean @Scope("singleton") public WxMpService wxMpService() { WxMpDefaultConfigImpl wxMpDefaultConfig = new WxMpDefaultConfigImpl(); wxMpDefaultConfig.setAppId(wxConfig.getAppId()); wxMpDefaultConfig.setSecret(wxConfig.getAppSecret()); wxMpDefaultConfig.setToken(wxConfig.getToken()); WxMpService wxService = new WxMpServiceImpl(); wxService.setWxMpConfigStorage(wxMpDefaultConfig); return wxService; } @Bean public WxMpMessageRouter messageRouter() { // 创建消息路由 final WxMpMessageRouter router = new WxMpMessageRouter(wxMpService); // 添加文本消息路由 router.rule().async(false).msgType(WxConsts.XmlMsgType.TEXT).handler(textHandler).end(); // 添加关注事件路由 router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT).event(WxConsts.EventType.SUBSCRIBE).handler(subscribeHandler).end(); // 添加扫码事件路由 router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT).event(WxConsts.EventType.SCAN).handler(scanHandler).end(); return router; } }handler/TextHandler.java/** * @author Hygge * @date 2022/08/20 * @description 用來處理文本消息的路由 */ @Component public class TextHandler implements WxMpMessageHandler { @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMpXmlMessage, Map<String, Object> map, WxMpService wxMpService, WxSessionManager wxSessionManager) throws WxErrorException { // 接收的消息内容 String inContent = wxMpXmlMessage.getContent(); // 响应的消息内容 String outContent; // 根据不同的关键字回复消息 if (inContent.contains("游戏")) { outContent = "仙剑奇侠传"; } else if (inContent.contains("动漫")) { outContent = "进击的巨人"; } else { outContent = inContent; } // 构造响应消息对象 return WxMpXmlOutMessage.TEXT().content(outContent).fromUser(wxMpXmlMessage.getToUser()) .toUser(wxMpXmlMessage.getFromUser()).build(); } } handler/ScanHandler.java/** * @author Hygge * @date 2022/08/20 * @description 用來處理用戶掃碼登錄的事件 */ @Component @Slf4j public class ScanHandler implements WxMpMessageHandler { @Resource private UserService userService; @Resource private RedisTemplate<String, Object> redisTemplate; @Override public WxMpXmlOutMessage handle(WxMpXmlMessage wxMpXmlMessage, Map<String, Object> map, WxMpService wxMpService, WxSessionManager wxSessionManager) throws WxErrorException { log.info("ScanHandler调用"); // 获取YYYY-MM-DD HH:MM:SS格式的时间 String content = "您在" + (new DateTime().toString("yyyy-MM-dd HH:mm:ss")) + "通过微信扫码登录PaperFF网站,感谢您的使用。"; // 获取场景值 String openId = wxMpXmlMessage.getFromUser(); // 判断用户是否存在 User user = userService.getUserByOpenId(openId); if (user == null) { // 如果用户不存在,则创建用户 user = userService.createUserByWeChat(openId); } // 将场景值和用户信息存入redis redisTemplate.opsForValue().set(wxMpXmlMessage.getEventKey(), user, 2, TimeUnit.MINUTES); return WxMpXmlOutMessage.TEXT().fromUser(wxMpXmlMessage.getToUser()).toUser(wxMpXmlMessage.getFromUser()) .content(content).build(); } }订阅(关注)的路由逻辑跟扫码的一样。4.配置消息接口地址:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/loginController/WechatLoginController.java/** * @author Hygge * @date 2022/08/13 * @description 微信登录的控制器 */ @Slf4j @RestController @RequestMapping("/login/wechat") public class WechatLoginController { @Resource private WxConfig wxConfig; @Resource private RedisTemplate<String, Object> redisTemplate; @Resource private WxMpService wxMpService; @Resource private WxMpMessageRouter wxMpMessageRouter; @Resource private WeChatLoginService weChatLoginService; /** * 获取登录二维码 * * @return 登录二维码 */ @GetMapping("/getQrCode") public R getQrCode() { try { // 获取AccessToken String accessToken = weChatLoginService.getAccessToken(); String getQrCodeUrl = wxConfig.getQrCodeUrl().replace("TOKEN", accessToken); // 这里生成一个带参数的二维码,参数是scene_str String sceneStr = MyStringUtil.getRandomString(8); String json = "{\"expire_seconds\": 120000, \"action_name\": \"QR_STR_SCENE\"" + ", \"action_info\": {\"scene\": {\"scene_str\": \"" + sceneStr + "\"}}}"; String result = HttpClientUtil.doPostJson(getQrCodeUrl, json); JSONObject jsonObject = JSONObject.parseObject(result); jsonObject.put("sceneStr", sceneStr); return R.ok().put("data", jsonObject); } catch (Exception e) { e.printStackTrace(); return R.error(e.getMessage()); } } /** * 根据二维码场景值获取用户的openId => 获取用户信息 * * @param eventKey * @return */ @RequestMapping("/getOpenId") public R getOpenId(@RequestParam String eventKey) { if (Boolean.FALSE.equals(redisTemplate.hasKey(eventKey))) { return R.error("等待用户扫码"); } User user = (User) redisTemplate.opsForValue().get(eventKey); redisTemplate.delete(eventKey); return R.ok("登录成功").put("data", user); } /** * 微信官方的回调处理 * 官方文档:<a href="https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html">...</a> * * @param request 微信发送的请求参数 */ @RequestMapping(value = "/message", produces = "application/xml; charset=UTF-8") public String handleMessage(HttpServletRequest request, HttpServletResponse response) throws Exception { //获取微信请求参数 String signature = request.getParameter("signature"); String timestamp = request.getParameter("timestamp"); String nonce = request.getParameter("nonce"); String echoStr = request.getParameter("echostr"); if (!wxMpService.checkSignature(timestamp, nonce, signature)) { throw new IllegalArgumentException("非法请求,可能属于伪造的请求!"); } else { // 验签通过,直接返回echostr // response.getWriter().write(echoStr); // return null; // 解析消息体,封装为对象 WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(request.getInputStream()); WxMpXmlOutMessage outMessage; try { // 将消息路由给对应的处理器,获取响应 outMessage = wxMpMessageRouter.route(inMessage); } catch (Exception e) { log.error("微信消息路由异常", e); outMessage = null; } // 将响应消息转换为xml格式返回 response.getWriter().write(outMessage == null ? "" : outMessage.toXml()); return null; } } }WeChatLoginServiceImpl.java/** * @author Hygge * @date 2022/08/21 * @description */ @Service public class WeChatLoginServiceImpl implements WeChatLoginService { @Resource private WxConfig wxConfig; @Resource private WxMpService wxMpService; @Override public String getAccessToken() { String getTokenUrl = wxConfig.getTokenUrl() .replace("APPID", wxConfig.getAppId()).replace("SECRET", wxConfig.getAppSecret()); String result = HttpClientUtil.doGet(getTokenUrl); JSONObject jsonObject = JSONObject.parseObject(result); return jsonObject.getString("access_token"); } @Override public WxMpUser getUserInfoByOpenid(String openId) { return wxMpService.getUserService().userInfo(openId); } }5.前端部分代码import {request, METHOD} from '@/api/request.js' import {SCAN_WECHAT_CODE, POLL_WECHAT_CODE} from "@/services/api.js"; import loadQrCode from "@/assets/images/qrcode_loading.gif" import loseQrCode from "@/assets/images/lose.png" export default { name: 'Login', data() { return { loginType: 'scanCode', qrcode: loadQrCode, sceneStr: '', t: 0, } }, methods: { toggleLoginType() { this.loginType = this.loginType === 'scanCode' ? 'input' : 'scanCode'; }, async getQrCode() { window.clearInterval(this.t); let response = await request(SCAN_WECHAT_CODE, METHOD.GET) this.qrcode = 'https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=' + response.data.data.ticket; this.sceneStr = response.data.data.sceneStr; // 开始监听轮询 this.t = window.setInterval(this.getOpenId, 6000); window.setTimeout(() => { this.qrcode = loseQrCode; window.clearInterval(this.t); }, response.data.data.expire_seconds) }, async getOpenId() { let response = await request(POLL_WECHAT_CODE, METHOD.GET, { eventKey: this.sceneStr }) if (response.data.code === 200) { window.clearInterval(this.t); console.log(response.data.data); } } }, mounted() { this.getQrCode() } }方式二:登录常见方式..用到在更新引用1.WxJava - 微信开发 Java SDK:https://github.com/Wechat-Group/WxJava2.微信公众平台 - 测试号管理 : https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login3.Sunny-Ngrok内网转发内网穿透 - 国内内网映射服务器 : https://www.ngrok.cc/ 4.WxJava微信公众号开发实战:https://baobao555.tech/archives/535.springboot前后端分离-使用微信扫码登录(后端):https://blog.csdn.net/xiaoping__/article/details/1242588816.微信公众平台开发概述 : https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html7.内网穿透工具--Sunny-Ngrok讲解:https://blog.csdn.net/weixin_44563573/article/details/1209075278.程序员大阳 - 微信公众号开发 :[https://blog.csdn.net/woshisangsang/category_11369636.html](
2022年08月21日
980 阅读
0 评论
1 点赞
2022-08-12
初探Laravel Starters Kits的Breeze&Vue
简介Breeze 是官方推荐的起手套装,内建有登入、注册、忘记密码等常用的用户功能,另外可以选择使用 Vue 或者 React 来建立SPA单页页面。首先,Breeze 适用于专门初始化的工具,如果项目已经开发到一半的话可能会有冲突,所以不适用。最好建立一个初始化的工程。创建工程1.创建项目如果是8或更低的版本中前端资源打包使用了mix执行npm install 和 npm run dev后,页面无法正常解析,属于是坑了解决方案:https://learnku.com/laravel/t/69192所以避免踩坑直接采用了最新版的v 9.3.3的Laravelcomposer create-project laravel/laravel=9.*.* --prefer-dist projectName2.安装breezecomposer require laravel/breeze --dev官方文档:https://laravel.com/docs/9.x/starter-kits#laravel-breeze-installation3.安装breeze & React/Vuephp artisan breeze:install vue # Or... php artisan breeze:install react php artisan migrate npm install npm run dev4.查看效果需要跑两个控制台图中左侧运行服务端:php artisan serve右侧运行前端:npm run dev访问http://127.0.0.1:8000查看!自带了登录、注册功能,单页应用全程无刷新,nice!架构分析探索一下 Laravel 是如何实现这些的1.浅析路由和组件首先看到 routes/auth.php,这里定义了使用者登录之前的页面路由与 API,包含注册、登入登出、忘记密码等。以 Get/login 为例,由 AuthenticatedSessionController 的 create 方法处理请求后回传登录页面。 在这之前加上了 guest 这个 middleware,如果请求带有已登录的状态,则不回传页面而直接跳到首页。Route::middleware('guest')->group(function () { // ... Route::get('login', [AuthenticatedSessionController::class, 'create']) ->name('login'); // ... });看看 AuthenticatedSessionController 的 create方法做了什么/** * Display the login view. * * @return \Inertia\Response */ public function create() { return Inertia::render('Auth/Login', //resources\js\Pages\Auth\Login.vue 页面组件 // 页面组件的Props参数 [ 'canResetPassword' => Route::has('password.request'), 'status' => session('status'), ]); }再看一下对应的Vue组件<script> defineProps({ canResetPassword: Boolean, status: String, }); </script> <template> ... <div v-if="status" class="mb-4 font-medium text-sm text-green-600"> {{ status }} </div> ... ... <Link v-if="canResetPassword" :href="route('password.request')" class="underline text-sm text-gray-600 hover:text-gray-900"> Forgot your password? </Link> ... </template>瞬间明白,php中可以直接返回信息回传给Inertia.js进行渲染页面,组件中进行defineProps声明后即可使用。2.Inertia原理這邊簡單說下 Inertia.js 的運作方式,最初連上網站的時候,會回傳一個帶有 Inertia 功能的全頁應用,而這個應用裡所有的 link 都會經過 Inertia 的處理,當點擊 link 時並不會直接跳轉網址而是變成發送一個 XHR 到 Laravel ,並經由 API 回傳 Inertia 渲染的畫面元件,進行畫面的更新。而說是 API 渲染元件其實有點不太準確,收到請求後 Inertia Render 會將 Vue/React 元件轉換成 JSON 並回傳(不是 Html),再經由前端的 Inertia 解析後重新渲染部分的畫面,達到類前端 App 的效果,所以實際的渲染還是發生在前端,API 只是提供資料。另外這個方法前端是沒有路由器的,Laravel 伺服器提供畫面的路由器替代了這部分。本段来自:https://ithelp.ithome.com.tw/articles/10267034?sc=iThelpR3.Vue目录结构进一步学习留个官方文档入口: https://laravel.com/docs/9.x/vite引用1.Laravel8.5使用套件laravel breeze ,所有页面版式都未正确加载:https://learnku.com/laravel/t/691922.Laravel Starter kits :https://laravel.com/docs/9.x/starter-kits#introduction3.使用 Breeze 建立基礎專案框架:https://ithelp.ithome.com.tw/articles/10267034?sc=iThelpR4.Migrating from Laravel Mix to Vite:https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md#migrating-from-laravel-mix-to-vite5.Bundling Assets (Vite):https://laravel.com/docs/9.x/vite
2022年08月12日
551 阅读
0 评论
0 点赞
2022-07-25
Redmi k40 Gaming Root
{mtitle title="本文目录"/}Redmi k40 Gaming Root注意:刷Root有变砖、无限重启、卡米等各种风险操作前请先备份好数据,解锁或刷机后的第一次开机时间较长,请等待10分钟左右。前言没有Root的机器太不方便了。一是因为在手机上抓app的包由于证书安装不了,关键请求拿不到。二是不能对Android.data.xxx应用数据文件夹的内容进行操作,就很烦。于是冒着变砖的风险 直接就开刷了1.解锁Bootloader这种方式解锁是需要等待几天时间的。解锁时候数据会被清除,请一定要备份好数据。解锁成功后:手机会自动退出FashBoot模式并重启,开机动画上方会有一个打开了的锁图标解锁工具下载:http://www.miui.com/unlock/index.html解锁Bootloader的步骤:1.在需要解锁的设备中登录已经具备解锁权限的小米账号,并进入“设置 -> 开发者选项 -> 设备解锁状态”中绑定账号和设备; 2.绑定成功后,手动进入Bootloader模式(关机后,同时按住开机键和音量下键); 3.下载手机解锁工具(解锁工具官网),在PC端的小米解锁工具中,登录相同的小米账号,并通过USB连接手机; 4.点击PC端解锁工具的“解锁”按钮,根据提示信息等待指定时间后再次尝试或者立即解锁;1.1 解锁Bootloader过程中可能遇到的问题:Q:解锁工具提示“账号设备不一致”是怎么回事? A:这是在解锁过程中没有通过账号与设备验证,解决办法是先将手机升级到最新的稳定版或者从稳定版卡刷到最新的开发版,在待解锁的设备和解锁工具上要登陆同一个账号,并进入“设置 -> 开发者选项 -> 设备解锁状态”中绑定账号和设备。 Q:解锁工具提示“无法获取手机信息”是怎么回事? A:这种情况一般是电脑上的设备驱动没有装好,可以尝试重插USB线或者换个USB接口或者换根USB线来等待电脑慢慢安装驱动,或在工具右上角驱动安装模块中主动安装驱动。 Q:解锁失败显示“账号与设备的绑定时间太短,xxx个小时后再解锁” A:在售的新机型一般需要等待,用户账号安全评分较低的需要等待,等待时间目前是7天起,如果本年度解锁手机数超过2台,等待时间会相应增长。 Q:解锁失败显示“此账号本月解锁次数达到上限” A:一个小米账号每月限制解锁一台设备。 Q:解锁失败显示“此账号本年累计解锁次数已达上限” A:一个小米账号每年限制解锁4台不同设备。 Q:解锁失败显示“账号权限不足或者账号受限” A:账号存在安全风险,无法处理解锁操作,建议更换账号。 Q:解锁失败显示“未知错误-1” A:网络异常,请更换时间段或更换网络进行解锁。2.下载Magisk一开始我也不知道这东西干什么的,可以简单理解为是管理Root权限的,其他应用需要Root权限会向它申请。我下载的版本是: V24.2官方Github : https://github.com/topjohnwu/Magisk/releases所有的Magisk版本需要注意:高通设备的安卓11:官方版本的Magisk(V23版本和V24版本)高通设备的安卓12::官方版本的Magisk(V24版本)大部分联发科设备的安卓11:官方版本的Magisk(V23版本)特殊机型(红米K40游戏增强版,Note10Pro)的安卓11:使用专用的Magisk 或 官方Magisk(V24版本)特殊机型(红米K40游戏增强版,Note10Pro)的安卓12:官方版本的Magisk(V24)3.下载线刷包下载网站:https://xiaomirom.com/找到自己手机型号和目标版本Redmik40 Gaming 直达 :https://xiaomirom.com/rom/redmi-k40-gaming-poco-f3-gt-ares-china-fastboot-recovery-rom/3.1 刷入Magisk参考的酷安文章,作者讲的很详细了,不再搬运:【小白向】为你的设备刷入Magisk(面具/脸谱)的Root:https://www.coolapk.com/feed/34261961?shareKey=OTBjYmYwZmNlYjc0NjJkZTJhYzA~&shareFrom=com.coolapk.market_12.3.2文章用到的工具下载:{cloud title="刷机工具压缩包" type="bd" url="https://pan.baidu.com/s/1To1eV06-0NrKHavktxHtuw?pwd=k4kz " password="k4kz"/}4.过程操作出错刷回正常机器百度上的操作太乱太杂了,踩坑的时候导致手机无限重启了,只能刷回正常的重新开始。前提条件:1.准备线刷包(第三小节),解压到全英的目录2.下载MiFlash:https://miuiver.com/miflash/刷机步骤(MiFlash的使用):http://www.winwin7.com/JC/21571.html4.1 MiFlash 错误 Not catch checkpoint 解决方法在使用 MiFlash 刷机时界面显示下图错误,但手机已完成刷机并自动重启进入系统,测试使用也没有发现异常。error:Not catch checkpoint (\$fastboot -s .*lock), flash is not done4.1.1 问题原因导致这个问题的原因是,新版 MiFlash 在刷机完成后会检测手机是否已上 BL 锁。如果您选择“全部删除”或“保留用户数据”这两种刷机模式,因为没有锁定 BL 锁,就会触发这个错误显示。由于这个检查是在刷机完成后进行的,所以实际上对手机并没有影响,这是 MiFlash 本身的问题。4.1.2 解决方法如果想要避免这个问题,除了使用“全部删除并lock”刷机模式外(注:如果刷海外版固件,请勿选择此刷机模式,不然会因 BL 区域限制无法启动系统),也可以在 MiFlash 配置选项里关闭这个检查。具体请在菜单栏点击“Configuration”,点击“MiFlash Configuration”,在弹出窗口删除 CheckPoint 里的 \$fastboot -s .*lock 文本。保持设置后再次刷机就不会遇到 error:Not catch checkpoint (\$fastboot -s .*lock), flash is not done 问题了。参考1.[教程] 小米手机解锁 Bootloader 教程和常见问题 : https://web.vip.miui.com/page/info/mio/mio/detail?postId=17982230&app_version=dev.200512.【小白向】为你的设备刷入Magisk(面具/脸谱)的Root : https://www.coolapk.com/feed/34261961?shareKey=OTBjYmYwZmNlYjc0NjJkZTJhYzA~&shareFrom=com.coolapk.market_12.3.23.MiFlash怎么用?小米刷机工具MiFlash使用方法:http://www.winwin7.com/JC/21571.html4.MiFlash 错误 Not catch checkpoint 解决方法:https://miuiver.com/miflash-error-not-catch-checkpoint/5.TUTU-整合magisk模块、刷机资源和教程及k40g/note11pro+的boothttps://miui.wtututu.top/
2022年07月25日
1,172 阅读
0 评论
0 点赞
2022-07-25
搞明白C#的Async和Await
本文参考: bilibili视频{bilibili bvid="BV1b54y1J72M" page=""/}讲的非常棒!{dotted startColor="#ff6c6c" endColor="#1989fa"/}需求引入 - 下载数据从不同的网站中下载很大的数据量,并且将数据结果呈现在UI客户端的一个经典的实际场景界面代码public partial class Form1 : Form { private readonly HttpClient httpClient = new HttpClient(); public Form1() { InitializeComponent(); } }界面中有一个同步下载按钮(SyncDownload)、异步下载按钮(AsyncDownload)、信息输出的文本框常量类public class Contents { public static readonly IEnumerable<string> WebSites = new string[] { "https://www.zhihu.com", "https://www.baidu.com", "https://www.weibo.com", "https://www.stackoverflow.com", "https://docs.microsoft.com", "https://docs.microsoft.com/aspnet/core", "https://docs.microsoft.com/azure", "https://docs.microsoft.com/azure/devops", "https://docs.microsoft.com/dotnet", "https://docs.microsoft.com/dynamics365", "https://docs.microsoft.com/education", "https://docs.microsoft.com/enterprise-mobility-security", "https://docs.microsoft.com/gaming", "https://docs.microsoft.com/graph", "https://docs.microsoft.com/microsoft-365", "https://docs.microsoft.com/office", "https://docs.microsoft.com/powershell", "https://docs.microsoft.com/sql", "https://docs.microsoft.com/surface", "https://docs.microsoft.com/system-center", "https://docs.microsoft.com/visualstudio", "https://docs.microsoft.com/windows", "https://docs.microsoft.com/xamarin" }; }一、同步下载/// 输出显示 private void ReportResult(string result) { Result.Text += result; } /// 同步下载按钮被单击 private void SyncDownload_Click(object sender, EventArgs e) { // 同步下载按钮点击后执行的代码 Result.Text = ""; // 为了测量程序的执行时间 var stopwatch = Stopwatch.StartNew(); // 启动下载的主函数 DownloadWebsitesSync(); // 输出函数执行的耗时情况 Result.Text += $"Elapsed time: {stopwatch.Elapsed}{Environment.NewLine}"; } /// 遍历所有站点 private void DownloadWebsitesSync() { foreach(var site in Contents.WebSites) { var result = DownloadWebSiteSync(site); ReportResult(result); } } /// 访问网站获取源代码 private string DownloadWebSiteSync(string url) { var response = httpClient.GetAsync(url).GetAwaiter().GetResult(); var responsePayloadBytes = response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult(); return $"Finish downloding data from {url}. Total bytes returned {responsePayloadBytes.Length}. {Environment.NewLine}"; }测试Finish downloding data from https://www.zhihu.com. Total bytes returned 35556. Finish downloding data from https://www.baidu.com. Total bytes returned 9508. Finish downloding data from https://www.weibo.com. Total bytes returned 0. Finish downloding data from https://www.stackoverflow.com. Total bytes returned 173712. Finish downloding data from https://docs.microsoft.com. Total bytes returned 132907. Finish downloding data from https://docs.microsoft.com/aspnet/core. Total bytes returned 90052. Finish downloding data from https://docs.microsoft.com/azure. Total bytes returned 428349. Finish downloding data from https://docs.microsoft.com/azure/devops. Total bytes returned 86931. Finish downloding data from https://docs.microsoft.com/dotnet. Total bytes returned 79622. Finish downloding data from https://docs.microsoft.com/dynamics365. Total bytes returned 56803. Finish downloding data from https://docs.microsoft.com/education. Total bytes returned 39267. Finish downloding data from https://docs.microsoft.com/enterprise-mobility-security. Total bytes returned 31372. Finish downloding data from https://docs.microsoft.com/gaming. Total bytes returned 61895. Finish downloding data from https://docs.microsoft.com/graph. Total bytes returned 45606. Finish downloding data from https://docs.microsoft.com/microsoft-365. Total bytes returned 68829. Finish downloding data from https://docs.microsoft.com/office. Total bytes returned 68829. Finish downloding data from https://docs.microsoft.com/powershell. Total bytes returned 57777. Finish downloding data from https://docs.microsoft.com/sql. Total bytes returned 55297. Finish downloding data from https://docs.microsoft.com/surface. Total bytes returned 40182. Finish downloding data from https://docs.microsoft.com/system-center. Total bytes returned 43407. Finish downloding data from https://docs.microsoft.com/visualstudio. Total bytes returned 31543. Finish downloding data from https://docs.microsoft.com/windows. Total bytes returned 27177. Finish downloding data from https://docs.microsoft.com/xamarin. Total bytes returned 56064. Elapsed time: 00:00:50.6908524缺点:使用了Foreach循环遍历网站请求数据占用了UI界面刷新的主线程,造成了UI刷新线程的阻塞在函数执行过程中,界面无法拖动,无法互动,用户体验效果差耗时50秒,由于是一个一个网址去请求,所以需要等待一个完成或失败后再去进行下一个请求,执行速度较慢!二、异步下载(一)-异步下载/// 异步下载按钮被单击 private void AsyncDownload_Click(object sender, EventArgs e) { Result.Text = ""; var stopwatch = Stopwatch.StartNew(); DownloadWebsitesAsync(); Result.Text += $"Elapsed time: {stopwatch.Elapsed}{Environment.NewLine}"; } /// 使用 async 用于提醒编译器 该方法中使用到了 Await 关键字 /// 对于希望使用Async和Await关键字的方法,返回值必须是Task或者是Task泛型 private async Task DownloadWebsitesAsync() { foreach (var site in Contents.WebSites) { var result = await Task.Run(() => DownloadWebSiteSync(site)); ReportResult(result); } }测试AsyncDownload_Click中并没有等待DownloadWebSitesAsync执行结束就打印了最后的执行时间相当于DownloadWebSitesAsync被忽略了。修改/// 异步下载按钮被单击 private async void AsyncDownload_Click(object sender, EventArgs e) { Result.Text = ""; var stopwatch = Stopwatch.StartNew(); await DownloadWebsitesAsync(); Result.Text += $"Elapsed time: {stopwatch.Elapsed}{Environment.NewLine}"; }总结经过如上编写,发现在测试异步下载时,UI界面线程不会阻塞,可以在下载时与界面进行交互。通过对比同步和异步两种下载方式,发现两者下载速度并没有太大差别。于是我们可以发现仅仅使用async和await并不能够提升我们处理数据的速度,给我们带来的仅仅是这个UI的响应更加及时。三、异步下载(二)-并发下载在上一节分发下载任务时,使用的是foreach进行遍历网址列表,逐个进行下载数据。虽然Ui Thread并没有被阻塞,但这并没有改变下载数据的策略,数据还是需要从这些网站中一个一个的下载。这样其实就很好理解,为什么在异步的模型中下载数据的时间总时长并没有被缩短。如果想缩短下载所有数据的总时间,那么就需要引入: 并行下载 即我们需要更多的线程并发的从这些网站中下载数据。/// 分发下载任务的主函数 private async Task DownloadWebsitesAsync() { /// 用于存储所有的Task List<Task<string>> downloadWebsiteTasks = new List<Task<string>>(); /// 将原来的下载完成一个再进行下一个,改成快速遍历所有站点,将每一个下载任务存入集合,集合中所有元素并发进行下载。 foreach (var site in Contents.WebSites) { downloadWebsiteTasks.Add(Task.Run(() => DownloadWebSiteSync(site))); } // 当集合所有任务都完成时,统一拿到Result Array<string>返回值 var results = await Task.WhenAll(downloadWebsiteTasks).Result; // 将所有results 全部打印出去。 foreach(var result in results) { ReportResult(result); } }注意:每次程序的第一次完成IO操作时,尤其是这种多线程从远端服务器需要用http下载数据,程序往往会经历一些冷启动的现象,原因是因为我们的 CLR 需要创建相应的Thread Pool来初始化Thread,而初始化每一个Thread也是需要时间的,并且完成这些Http Connection的TCP Connection的建立也是需要时间的。因此程序运行的第一次下载耗时不太稳定,会花费额外的时间,如果想要查看稳定的数据下载,需要多次Run一下这个实验的结果。测试观测到 异步下载+并行下载的时间稳定在2s左右修改减少由于初始化很多线程而造成的速度不稳定冷启动时间/// 访问网站获取源代码 更改为异步操作,命名遵循范式 private async Task<string> DownloadWebSiteAsync(string url) { var response = await httpClient.GetAsync(url); // 直接调用异步方法 var responsePayloadBytes = await response.Content.ReadAsByteArrayAsync(); // 直接调用异步方法 return $"Finish downloding data from {url}. Total bytes returned {responsePayloadBytes.Length}. {Environment.NewLine}"; } private async Task DownloadWebsitesAsync() { List<Task<string>> downloadWebsiteTasks = new List<Task<string>>(); foreach (var site in Contents.WebSites) { // 这时候不需要一个额外的Thread来进行等待 // 所以不再需要Tash.Run,可以直接调用方法。 downloadWebsiteTasks.Add(DownloadWebSiteAsync()); } var results = await Task.WhenAll(downloadWebsiteTasks).Result; foreach(var result in results) { ReportResult(result); } }程序运行后的第一次下载:耗时2s
2022年07月25日
316 阅读
0 评论
0 点赞
2022-07-21
《嫌疑人X的献身》- 读书记录
简介《嫌疑人X的献身》(日语:容疑者Xの献身)是日本作家东野圭吾的长篇推理小说,也是“伽利略系列”(ガリレオシリーズ)的第三本小说。2003年起以“嫌疑犯X”为题于《ALL读物》连载。2005年8月于文艺春秋出版,出版的时候改成现在的标题。2011年英文版出版,译名《The Devotion of Suspect X》。本作是东野圭吾发表作品迄今被最多语言翻译代理的作品。人物石神 哲哉(石神いしがみ 哲哉てつや,Ishigami Tetsuya)高中数学老师,与花冈靖子是同一栋公寓中的邻居。由于顶着稀疏的头发与圆脸,使得看起来较老,实际和汤川、草薙是帝都大学的同届学生。大学时代被称为“达摩石神”,和汤川一样都是非常有才华的天才。原本想当一个数学家,却因为家里的关系而无法继续进行研究,最终只能在高中任教。暗中喜欢花冈靖子。花冈 靖子(花岡はなおか 靖子やすこ,Hanaoka Yasuko)在赤坂当过陪酒女郎,之后在“弁天亭”担任店员。第一段婚姻失败,与第二任丈夫富㭴离婚后过着颠沛流离的生活。对总是连累美里感到很抱歉,工藤是她当陪酒女郎时的朋友。花冈 美里(花岡はなおか 美里みさと,Hanaoka Misato)是靖子与第一任丈夫所生的独生女,目前就读国中,参加羽毛球社。虽然看似柔顺,但也有拿铜制花瓶殴打富㭴这样强势的一面。由于她也被牵连进来,因此靖子才听从石神的计划。由于察觉到石神对母亲的爱恋,因此对母亲与工藤之间的关系颇有微词。富㭴 慎二(富樫とがし 慎二しんじ,Togashi Shinji)靖子的第二任丈夫,与美里没有血缘关系。原本表面上是彬彬有礼的上班族,因亏空公款被公司开除后暴露卑劣的本性。与靖子离婚之后还一直纠缠不清。工藤 邦明(工藤くどう 邦明くにあき,Kudou Kuniaki)靖子之前工作的酒店“玛丽安”的常客。对靖子有好感,希望与靖子结婚。汤川 学(湯川ゆかわ 学まなぶ,Yukawa Manabu)本小说的主人公。帝都大学物理学的精英副教授,隶属理工学部物理学科第十三研究室。毕业于帝都大学理工学部。拥有物理学领域的天才头脑,此外对杂学知识也有涉猎。洞察力非常敏锐,曾通过观察猜中陌生女性的职业。凭借著这些能力,汤川靠少量的信息就能够帮助身为警察的大学同期草薙将棘手的案件用物理知识解决,因此被搜查一课的人崇拜地称为“伽利略老师”,但他本人却对此称呼感到厌恶。对外从不展现自己的真面目,让人难以得知他私下的为人。常在大学研究至深夜,晚饭也会在大学内完成(但有时也会自己煮食)。在硕士研究生二年级秋天的时候,其设计的“磁界齿轮”备受瞩目,美国企业的咨询蜂拥而至,但其实现应用条件异常苛刻,以至最终沦为纸上谈兵。磁界齿轮的诞生源自于汤川将之前的失败研究获得的经验应用于其他领域研究而得出来的结果。性格偏执,经常会在草薙为了引他提起对案件兴趣而说的话里挑骨头。另外,无论外界环境如何,他也丝毫不会受到影响,保持冷静思考,并不会掺入个人感情。兴趣是羽毛球,还在羽毛球部的时候就是高手,如今也获得过比赛的优胜。喜欢的东西是即溶咖啡,而且是喜欢到对它的历史谙熟的程度。草薙拜访的时候,也是用不怎么干净的马克杯冲泡淡味的即溶咖啡来招待。害怕小孩。理由是“小孩是不合逻辑及疲于应付的对手”,以至于只是和小孩说话也会产生荨麻疹的过敏症状。因此调查案件的时候往往由草薙负责和小孩对话,有疑问的时候也通过草薙向小孩发问。虽然认为女性中也存在拥有逻辑思维的人,但却没有交往的对象。另外汤川虽是主角,以他为主体的场面描写却较少,作品大部分都是以警察或者嫌疑人(或引发现象的现场周围的人)的视角来描述事件。草薙tì 俊平(草薙くさなぎ 俊平しゅんぺい,Kusanagi Shunpei)隶属警视厅搜查一课,由巡查部长升至警部。虽不是主角,但其实他才是本小说实际上的中心人物。豆评当深爱一个人的时候,这个世界只分为两种。她,其他。文章并不长,备受好评且位于东野圭谷的书单中第一位。故事从靖子母女失控杀人说起。被杀者是靖子的前夫,一个十足的混混。游手好闲、挥霍度日,没钱的时候向靖子伸手,若是不给,自然少不得一顿狠揍。忍无可忍的靖子与其离婚,为躲避他的纠缠更是几番搬家。我想平静度日应该是这对母女最大的心愿,但这样的心愿竟也是无法被满足的。那个混混再一次找上了母女二人。而这一次,愤怒的母女冲动之下将他杀了。坦白说这样的人被杀我并不同情,我同情的是这对母女必须为此而承担的后果。在她们惊惶无措之下,隔壁邻居,一直暗恋着靖子的石神伸出了援手。他用自己过人的天资设了一个局,让警方无论如何抓不准案子的关键。我以为如果没有汤川,石神的计谋应该是会成功的。但显然让有罪之人逍遥法外这样的情节不会出现在东野圭吾的书里,哪怕那个罪人多么值得同情。我们所知的真相是靖子母女杀了富樫慎二,石神哲哉帮其掩盖真相。而当故事一步步推进,真相之后的真相被一点点揭开,内心的震撼实难言表。我可以想象一个人会因为爱另一个人而去撒谎、去欺骗、去顶罪甚至去杀人……但我不曾想到的是石神会因为想要帮靖子掩盖杀人真相而去杀害另一个无辜人……爱情是什么?是索取还是付出?而石神的爱情应该像徐志摩说的那样“不求有结果 ,不求同行,不求曾经拥有,甚至不求你爱我”,因为他说“一个人只要好好活着 就足以拯救某人”。故事的最后,真相大白于天下,靖子崩溃了,石神也崩溃了。不是推理迷,事实上有限的智商也不足以进行极富逻辑性地推理,至今也只看过《白夜行》与《犯罪嫌疑人X的献身》两部。但就这两本作品里,我看到了最丑陋的人性、最无私的奉献、最深情的爱意以及最绝望的结局。结束豆评出处: https://book.douban.com/review/7684695/Wiki:https://zh.wikipedia.org/zh-sg/%E5%AB%8C%E7%96%91%E7%8A%AFX%E7%9A%84%E7%8D%BB%E8%BA%AB
2022年07月21日
375 阅读
0 评论
2 点赞
2022-07-10
记录一次调用OCR验证码识别库的过程
1.前言最近在写Python项目中用到了其他网站的接口,请求的时候对方接口需要验证码,之前使用的一直是联众打码平台( https://www.jsdati.com/ ),没想到今天访问的时候已经打不开了...{lamp/}谷歌了一下找到了这个库,名字挺有意思哒ddddocr(带带弟弟OCR): https://github.com/sml2h3/ddddocr2.Python版本配置我平常用的是普通的Python3.10+,安装不了这个库,找降低Python版本的方法也没找到。于是就卸载了Python换成带有版本管理的Anaconda,官网: https://www.anaconda.com/ 安装的时候不建议也不需要配置环境变量,控制台使用Anaconda自带的就好基础命令:# 1.创建新环境并指定环境的Python版本 conda create --name env_name python=version 例如: conda create --name python36 python=3.6 # 2.激活环境 activate env_name # 3.关闭环境 deactivate env_name # 4.删除环境 conda env remove -n env_name # 5.显示所有环境 conda env list # 6.查看anaconda中已经存在的镜像源 conda config --show channels # 7.添加镜像源(永久添加) conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/ # 8.设置搜索时显示通道地址 conda config --set show_channel_urls yespycharm创建Virtualenv,指定刚才创建的conda环境中python.exe解释器。3.安装ddddocrpip install ddddocr4.使用import ddddocr ocr = ddddocr.DdddOcr(old=True,show_ad=False) with open('stuExam.jpg', 'rb') as f: image = f.read() res = ocr.classification(image) print(res)比较清晰的是可以识别出来的,测试了三个验证码,完全识别正确的只有一张,看来是无法投入到当前项目中使用了。5.参考链接文安哲的博客-ddddocr作者: https://wenanzhe.com/阿迪(GIF)点选验证码识别测试页面:http://146.56.204.113:19199/preview
2022年07月10日
579 阅读
1 评论
1 点赞
1
...
7
8
9
10