跳转到内容
彼岸论坛

小天管理

管理员
  • 内容数

    15882
  • 注册日期

  • 最后上线

  • 得奖次数

    1

小天管理 发表的所有内容

  1. 坐标成都。今天同事说网站打不开了,看不到文档。 请问是遇到什么变故了吗?临时的还是永久的?
  2. 前言 最近更新了我的系列文章,其中有一部分是关于 JavaScript 语言中“点表示法”的使用。 文章中有 5 个工具函数是纯JavaScript的,我觉得不仅仅是小程序项目用得上,其他前端 JS 项目也应该用得上。 我把文章中的这一部分单独整理出来,给需要写前端代码的朋友参考参考。 你可以直接在 Github 项目 中的前端 utils.js 文件中找到这 5 个工具函数。 下面是文章节选,完整的文章列表可以在 Github 项目 中查看。 点表示法a.b.c 用点表示法给对象赋值:putValue函数 想象一下,如果a是一个对象,你要给a.b.c.d赋值为 1 ,你会这样写? let a // 通过某种方式获得的对象 if (!a.b) { a.b = {} } if (!a.b.c) { a.b.c = {} } a.b.c.d = 1 拜托,大学生才这样写。为此,我们引入了putValue函数: utils.putValue(a, 'b.c.d', 1) putValue函数会自动创建a.b、a.b.c中间对象,它的声明如下: /** * 向对象中按照路径赋值,如果路径上的中间对象不存在,则自动创建。 * @param {Object} obj - 目标对象。 * @param {string} key - 属性路径,支持'a.b.c'形式。 * @param {*} value - 要设置的值。 * @param {Object} [options={}] - 可选参数。 * - {boolean} remove_undefined - 如果为 true 且 value 为 undefined ,则删除该属性。 * @throws {Error} 如果 obj 为 null 或 undefined ,或路径不合法(如中间非对象)则抛出异常。 */ putValue(obj, key, value, {remove_undefined = true} = {}){ // ... } 使用这个函数有两个地方要注意,一个是如果路径上的中间对象不是对象,会抛出异常。例如a.b=2,这里b不是对象,此时会抛出异常。 另一个是如果value是undefined,则会删除该属性。例如下面的代码: utils.putValue(a, 'b.c.d', undefined) console.log(a) // {b: {c: {}}},putValue 会自动创建中间对象,但不会自动删除空对象 但若你真想赋值为undefined,可设置remove_undefined参数为false。 读取对象属性值:pickValue函数 对应的,如果要获取a.b.c.d的值,可以使用pickValue函数: utils.putValue(a, 'b.c.d', 1) const value = utils.pickValue(a, 'b.c.d') console.log(value) // 1 当然你也可以直接使用javascript的原生语法: const value = a.b?.c?.d 这两种写法,当中间路径不存在时,均会返回undefined。 但a.b?.c?.d这种写法是硬编码,而在实际开发中路径可能是动态的。例如用户要修改一个配置项,这个配置可能是user_config.font.size也可能是user_config.page.color.background,如果使用硬编码的方式,可能会写出这样的代码: if (key === 'font.size') { user_config.font.size = value } else if (key === 'page.color.background') { user_config.page.color.background = value } // 更多的 if 语句... 这样写显然不够优雅,看看putValue与pickValue的组合用法。 // 写入用户配置: utils.putValue(user_config, key, value) // 读取用户配置 const value = utils.pickValue(user_config, key) 不管key怎么变,一句话搞定,感觉一下子和大学生拉开差距了是吧? 向数组末尾添加元素:pushValue函数 在实际开发中,常有向数组末尾添加数据的需求。例如记录用户最近的评论,此时你可以使用pushValue函数: const user_data = {} // 用户数据 let comment = {content: '顶'} // 用户的评论 utils.pushValue(user_data, `articles.recent_comments`, comment) console.log(user_data) // {articles: {recent_comments: [{content: '顶'}]}} 在上面代码中,pushValue函数先是自动创建了user_data.articles.recent_comments数组,然后把comment添加到数组末尾。pushValue函数的声明如下: /** * 将值推入对象指定路径的数组中,若路径或数组不存在则自动创建。 * @param {Object} obj - 目标对象。 * @param {string} key - 数组属性的路径,支持'a.b.c'形式。 * @param {*} value - 要推入的值。 * @throws {Error} 如果路径不是数组,则抛出异常。 */ pushValue(obj, key, value){ // ... } 再次调用此函数,数组中就会有两个评论: comment = {content: '再顶'} utils.pushValue(user_data, `articles.recent_comments`, comment) console.log(user_data) // {articles: {recent_comments: [{content: '顶'}, {content: '再顶'}]}} 向对象中添加多个属性:putObj函数 前面我们使用putValue函数向obj对象写入了一个属性值,但如果你要写入很多个(例如 100 个)属性值,你可能会使用for循环: let user_config = {} let new_config_keys // 100 个新的配置项(数组) let new_config_values // 对应的 100 个值(数组) for (let i = 0; i < new_config_keys.length; i++) { utils.putValue(user_config, new_config_keys[i], new_config_values[i]) } 这样写没问题,但在实战中,你拿到的用户配置往往不是数组的形式,而很可能是一个对象,例如: let new_config = { font: { size: 16, 'family.first': 'Arial', 'family.second': 'sans-serif', }, 'page.color.background': '#fff', // ... } // 你对拿到的配置数据又进一步处理 new_config.update_time = new Date() 这种情况下你可以使用putObj函数一次性写入多个属性值: utils.putObj(user_config, new_config) console.log(user_config) /* 输出如下: { font: { size: 16, family: { first: 'Arial', second: 'sans-serif' } }, page: { color: { background: '#fff' } }, update_time: '...', } */ 注意putObj会自动处理上面new_config变量中各种路径的写法。putObj函数的声明如下: /** * 将一个对象的所有属性按路径添加到另一个对象中。 * @param {Object} obj - 目标对象。 * @param {Object} obj_value - 要添加的属性对象,键支持'a.b.c'形式的路径。 * @param {Object} [options={}] - 可选参数。 * - {boolean} remove_undefined - 如果为 true 且 value 为 undefined ,则删除该属性。 * @throws {Error} 如果 obj 为 null 或 undefined ,或路径不合法(如中间非对象)则抛出异常。 * * 注意 * 若 obj_value 中出现重复路径,则后者会覆盖前者。 * 如 obj_value = {a: {b: 1}, 'a.b': 2},则结果为 {a: {b: 2}} */ putObj(obj, obj_value, { remove_undefined = true} = {}) { // ... } 从对象中获取多个属性:pickObj函数 同样的,我们可以一次性读取多个对象的属性值。例如虽然小程序中的用户配置非常复杂,但当前页面仅关注背景颜色、字体大小等少量配置项,你可以这样使用pickObj函数: let user_config // 某个用户的所有配置 // 本页面需要关注的配置 const keys = ['page.color.background', 'font.size', 'font.family'] // 获取当前页面需要的配置 const curr_config = utils.pickObj(user_config, keys) console.log(curr_config) /* 输出如下: { 'page.color.background': '#fff', 'font.size': 16, 'font.family': { first: 'Arial', second: 'sans-serif' } } */ console.log(curr_config.font) // undefined 注意,传给pickObj函数的第二个参数是一个字符串数组,而不是对象。并且,pickObj返回的对象中,属性值不是以curr_config.font.size这样的形式返回,而是返回curr_config['font.size']。 当然,如果你想要curr_config.font.size这样的形式,可用putObj转换一下: let obj_config = utils.putObj({}, curr_config) console.log(obj_config.font.size) // 16 点表示法在微信小程序中实战演示 为什么要设计这几个函数?为什么要支持config.a.b.c与config['a.b.c']两种写法混用?为什么传给putObj的第二个参数是对象,而传给pickObj的第二个参数是字符串数组?为什么pickObj返回的对象属性值不是config.a.b.c这样的形式,而是config['a.b.c']? 因为这样设计符合实战需求,一句话解释就是:“这样好用”。 下面我们通过几个案例来演示这些函数在实战中的应用。 在 js 中设置用户配置 假设用户首次打开小程序,你需要设置用户默认字体大小为 16 ,背景颜色为白色。可以这样写: let user_config = {} utils.putValue(user_config, 'font.size', 16) utils.putValue(user_config, 'page.color.background', '#fff') 使用putValue设置后,你想修改字体大小和背景颜色?可以这样写: user_config.font.size = 18 user_config.page.color.background = '#000' 在 wxml 中实现修改用户配置 你可能会在 wxml 页面中实现多个配置项的修改,并且使用同一个函数来处理。这时你可以这样写: <button bind:tap="changeConfig" data-key="font.size" value="16" > <button bind:tap="changeConfig" data-key="page.color.background" value="#fff"> changeConfig(e){ const { user_config } = this.data const { key, value } = e.currentTarget.dataset // 从 wxml 中获得点表示法的 key 字符串,直接调用 putValue 函数 utils.putValue(user_config, key, value) // 修改背景色时顺便改一下字体颜色(两种写法混用) if (key === 'page.color.background' && value === '#fff') { user_config.page.color.font_color = '#000' } // 记录最近修改时间 user_config.update_time = new Date() } 在 wxml 中使用用户配置 要在页面中使用page.color.background与page.color.font_color的值,实现根据用户配置显示不同的颜色,可以这样写: <view style="background-color: {{color.background}}"> <text style="color: {{color.font_color}}"> Hello, WxMpCloudBooster! </text> </view> onLoad(){ const { user_config } = this.data const color = utils.pickValue(user_config, 'page.color') this.setData({color}) } 你看,我们传递给pickValue的key根据实际需求可长可短。 初始化默认的用户配置 你希望为每个用户设置一个默认的用户配置,并且你想用常规方式写(不使用点表示法)。可以这样: // 默认配置 const DEFAULT_CONFIG = { font: { size: 16, }, page: { color: { background: '#fff', font_color: '#000' } }, // 这里也可以使用点表示法 a.b.c ,但你不想这样写... } App({ initConfig(){ let { user_config } = this.data utils.putObj(user_config, DEFAULT_CONFIG) // user_config 的其他值会被保留 // 保存用户配置... } }) 注意,上面代码中user_config可能会有其他没有出现在DEFAULT_CONFIG中的配置项,这些配置项会被保留。 记录用户最近发表的内容 假如你已经实现了“记录用户最近发布的评论”功能,代码如下: <button bind:tap="append" data-key="articles.recent_comments" data-prop="comment" > 当用户点击这个按钮时,假设this.data中已经有一个comment对象,你可以这样添加评论: append(e){ const { user_data } = this.data const { key, prop } = e.currentTarget.dataset const value = this.data[prop] // prop === "comment" utils.pushValue(user_data, key, value) // 注意这里用的是 push } 上面这个append函数会把this.data.comment对象添加到user_data.articles.recent_comments数组的末尾。 然后,此时你希望再增加一个按钮,可以把最近的点赞数据this.data.like添加到user_data.articles.recent_likes数组的末尾,那么只需一句: <button bind:tap="append" data-key="articles.recent_likes" data-prop="like" > 完成了,你不需要修改append函数,只需要给data-key和data-prop属性设置不同的值即可。 可见,点表示法很大的目的是为了在wxml中可以方便地指定路径,并在js中方便地处理这些路径。 在页面中修改多个配置项 假设你有一个修改用户配置项的页面,wxml代码如下: <!-- 注意这里有一个 for 循环 --> <view wx:for="{{configs}}"> 配置名称:{{item.title}} 当前值:{{item.value}} 输入新值:<input type="text" /> 点击修改:<button bind:tap="changeConfig"/> </view> 上面代码使用了for循环,configs变量中有多少个值,就会显示多少个配置项。 为了实现在用户打开页面时显示的是用户的当前值(而不是默认值),你还需要从user_config中读取当前用户的配置值。 代码样例如下: // 代码中写死了需要修改的配置项以及默认值 configs = [ {title: '字体大小', key: 'font.size', value: 16}, {title: '背景颜色', key: 'page.color.background', value: '#fff'}, {title: '字体颜色', key: 'page.color.font_color', value: '#000'}, ] // 读取当前用户的配置值 const uc_obj = utils.pickObj(user_config, configs.map(item => item.key)) // 注意,这里的 uc_obj 是 uc_obj['font.size'] 这样的形式,而不是 uc_obj.font.size // 用户当前值覆盖默认值 configs.forEach(item => { item.value = uc_obj[item.key] }) this.setData({configs}) // 传给 wxml 页面显示 这样你就实现了修改多个配置项的页面,用户打开页面时显示的是用户当前的配置值。 提问:假设我们坚决不使用点表示法,且要实现上面这些功能,你要如何设计才能如此简单、高效? 让你的函数也支持点表示法 好了,目前我们花了不少篇幅介绍点表示法,这是因为后面我们会介绍更多的工具函数,而这些工具函数都支持点表示法的调用方式。 当你编写自己的工具函数时,你可以调用putValue、pickValue、pushValue、putObj、pickObj这 5 个函数,轻松地让你的工具函数也支持点表示法。如果你不知道如何实现,可以参考utils.js中其他函数的代码。 (文章节选完,如果你感兴趣的话可以看看 Github 项目)
  3. 有注册成功电商平台 faire 吗 或者有美国身份,对电商感兴趣 有赚钱的机会 欢迎私聊
  4. 不同版本的 gradle 指令改来改去,就算这是两年一改也很是折腾,另外,为什么包管理器一定要绑定 jdk 版本呢 python 同样一个包,兼容 3.8 而与 3.12 冲突? semVer 说好的小版本兼容呢?另外,项目 a 依赖的 b 跟 c 的上层依赖互相冲突,只好分两个 project 搞
  5. Spring RestTemplate 拦截器修改请求体导致的诡异问题 最近在工作中发现了 Spring 的一个"特性"(也许可以叫 Bug ?),反正我已经给 Spring 提了 PR ,等着看能不能合进去。 问题背景 最近在调用第三方 API 时,遇到了一个有意思的场景。整个调用流程大概是这样的: 先调用 /login 接口,发送 username 和 password ,对方服务返回一个 JWT 。 之后的每个请求接口都是标准格式,需要把 JWT 和请求参数放到一个 JSON 中,类似这样: { "token": "JWT-TOKENxxxxxx", "data": { "key1": "value1", "key2": "value2" } } 发送请求,然后拿到响应报文。 解决方案 为了避免在每个接口都重复封装 token ,我想到了用 org.springframework.http.client.ClientHttpRequestInterceptor 来拦截请求,统一修改请求体。 代码大概长这样: this.restTemplate = new RestTemplateBuilder() .requestFactory(() -> new ReactorNettyClientRequestFactory()) .interceptors((request, body, execution) -> { byte[] newBody = addToken(body); // 调用登陆获取 token ,修改入参 body ,添加 token return execution.execute(request, newBody); }) .build(); 诡异的问题 修改完成后,进入测试阶段,奇怪的事情就发生了:token 能正确获取,body 也修改成功了,但对方的接口一直报 400 ,Invalid JSON 。更奇葩的是,我把 newBody 整个复制出来,用独立的 Main 代码发送请求,居然一次就成功了。 深入源码 不服气的我只能往源码里找原因。从RestTemplate一路 Debug 到org.springframework.http.client.InterceptingClientHttpRequest.InterceptingRequestExecution#execute,发现了这么一段代码: @Override public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException { if (this.iterator.hasNext()) { //这里是在执行 interceptor 链,我的登陆和修改 body 接口就在这里执行 ClientHttpRequestInterceptor nextInterceptor = this.iterator.next(); return nextInterceptor.intercept(request, body, this); } else { // 上面的 interceptor 链执行完后,下面就是真实执行发送请求逻辑 HttpMethod method = request.getMethod(); ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method); request.getHeaders().forEach((key, value) -> delegate.getHeaders().addAll(key, value)); if (body.length > 0) { if (delegate instanceof StreamingHttpOutputMessage streamingOutputMessage) { streamingOutputMessage.setBody(new StreamingHttpOutputMessage.Body() { @Override public void writeTo(OutputStream outputStream) throws IOException { StreamUtils.copy(body, outputStream); } @Override public boolean repeatable() { return true; } }); } else { StreamUtils.copy(body, delegate.getBody()); } } return delegate.execute(); } } 在 Debug 到request.getHeaders().forEach这里时,我突然发现 request 里的Content-Length居然和body.length(被修改后的请求体)不一样。 问题根源 继续往上追溯,在org.springframework.http.client.AbstractBufferingClientHttpRequest中找到了这段代码: @Override protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException { byte[] bytes = this.bufferedOutput.toByteArrayUnsafe(); if (headers.getContentLength() < 0) { headers.setContentLength(bytes.length); } ClientHttpResponse result = executeInternal(headers, bytes); this.bufferedOutput.reset(); return result; } 原来Content-Length在执行拦截器之前就已经被设置了。但我们在拦截器里修改了body,导致对方接收到的 JSON 格式总是不对,因为Content-Length和实际的请求体长度不匹配。 解决问题 这时候为了先解决问题,就先在interceptor中重新赋值了Content-Length this.restTemplate = new RestTemplateBuilder() .requestFactory(() -> new ReactorNettyClientRequestFactory()) .interceptors((request, body, execution) -> { byte[] newBody = addToken(body); // 调用登陆获取 token ,修改入参 body ,添加 token request.getHeaders().setContentLength(body.length); // 重新设置 Content-Length return execution.execute(request, newBody); }) .build(); 测试后,问题解决了。 反思和改进 问题虽然解决了,但我琢磨了一下,虽然是我在拦截器中修改了 body ,但这个地方 Spring 应该还是有责任把错误的Content-Length修正的。 第一,Spring 的文档中没有明确写这里应该由谁来负责,是个灰色地带。 第二,我们用RestTemplate谁会自己设置Content-Length啊,不都是框架设置的吗,所以这里不也应该由框架来负责嘛。 思考完,周末找了个时间给 Spring 提了个 PR ,有兴趣的同学可以到这里看看。Update Content-Length when body changed by Interceptor 有一说一,虽然不是第一次提 PR ,但是还是感觉挺爽的,记录一下。 写的挺乱的,技术一般,大佬轻喷。
  6. 本人不太熟悉运维和安全方面的东西,然后现在买了一台 2c2g 的阿里云的 ecs 想要在这台服务器上部署自己写的一些页面然后还有一些对外的 node 服务 但是又考虑到服务器的安全性,还有部署时候的一系列繁琐的操作,就像找个工具统一弄一下 在网上看大家对 1Panel 和 雷池 的评价比较高 不过看了一下对硬件的要求,我的服务器如果同时安装两个,可能内存就撑满了(现在服务器用 docker 装了一个 1panel 和 mysql 就已经占一半了) 现在想问一下各位大佬,装哪一款比较好,或者说这两个根本就是不同纬度的东西,没有可比性 我目前能想到的需求主要是集中在下面两点: https 证书的更新啥的 怕服务器被攻击
  7. 打电话对方听不清,要开免提,天才吧说底部麦克风损坏(当时那 sb 还说的是扬声器),只能自己掏钱整机更换,更换之后保修期只有 90 天 私人维修店说 120 换个“送话器”就能搞定,球问各位靠谱吗 顺便吐槽下 apple store 服务够 low 的,那态度拽的跟你欠了它八百万似的
  8. #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> int g=100; int main() { static int x=666; pid_t pid = fork(); if (pid==-1){ printf("创建进程失败\r\n"); } if (pid==0){ printf("pid=%d g=%d,x=%d,g=%p,x=%p\r\n",pid,g,x,&g,&x); g=111; x=222; printf("我子进程 pid=%d,g=%d,x=%d,g=%p,x=%p\r\n",pid,g,x,&g,&x); }else{ printf("pid=%d,g=%d,x=%d,g=%p,x=%p\r\n",pid,g,x,&g,&x); g=666; x=777; printf("我是父进程 pid=%d,g=%d,x=%d,g=%p,x=%p\r\n",pid,g,x,&g,&x); } return 0; } 输出 pid=4502,g=100,x=666,g=0x601044,x=0x601048 我是父进程 pid=4502,g=666,x=777,g=0x601044,x=0x601048 pid=0 g=100,x=666,g=0x601044,x=0x601048 我子进程 pid=0,g=111,x=222,g=0x601044,x=0x601048 我的问题是: 子进程修改变量后,会重新开启一块新内存,再我重新修改变量值后,为什么在打印变量的地址还是相同的?
  9. 多少刀我忘了反正是 3 年付 0.5t 盘的初期活动鸡, 愿意出的老哥留个 tg? 适当溢价可
  10. 很久没更新翻译软件了,前段时间偶然知道 deepseek ,试用了下,添加到沉浸式翻译,翻译结果非常准确,读起来通顺,以前用过百度,通义千问,字节,腾讯,的 API 感觉都不是太完美,微软,Google 也差点,,,以前一直用的微软,,腾讯偶尔,,其他翻译都差点意思 还没试用过 openai 的翻译,,主要 api 并发问题不好解决,开翻译会员有太贵,, 请问网上那些卖中转 api 的,兼容 openai ,key 的是用 chat2api 项目开发的吗??他们那种怎么那么多账号,撸来的做负载?,,那种中转 api 根 CNY 一比一,不知道放到翻译 api 效果如何,,,
  11. 在阅读 ptrace 相关代码时,看到为了修改 openat 系统调用的 path 参数,在小于 sp 指针(或者叫高于栈顶位置)保存了新的 path 路径,然后将传递参数的寄存器指向了这个新的 path ,这样做为什么是没有问题的?一般文档上说,sp 指向栈顶,那么如果变量使用了没有被 sp 涵盖的内存空间(字符数组指向了小于 sp 指针的位置),这不会报错吗?
  12. 周围同事认为事假会“扣薪”,所以不到万不得已不会轻易请事假。但在本人的认知里,本来人就没去上班,没有当天的薪资很正常,故即使年假用光了也会经常以小时为单位请事假。(被调侃了下“没有生活压力~”) 关于这点认知的差异我稍感兴趣。可能是我一直以来都是按时薪去计算薪资,导致观念里认为自己的工作是计时的;但其他人或许不这么认为,他们大概更倾向于固定薪资(?),所以才会有因事假失去了当天的薪资属于被“扣薪”的观念。 综上,发帖调查统计下其他人的观念。
  13. 原脚本没有对剪贴板的内容进行 urlencode 处理,稍作修改了一下 https://gist.github.com/codexss/03ca2290a89d8becf6c39286b0af537c
  14. 今天想安装个新 Docker ,发现怎么都拉取不到镜像。然后想起来 Dockerhub 被墙了... 于是我把 NAS 的网关和 DNS 地址指向了旁路由网关,旁路由开了梯子。 但是还是不行,折腾了一圈后我把 NAS 的 ipv6 关了,然后就好了。 NAS 是威联通,旁路由是 openwrt+passwall 想请教一下各位,应该怎么设置调整才能 ipv6 和科学共存。因为需要 ipv6 访问 NAS
  15. Hee Labs 是一家美国硅谷 AI 教育公司。我们的旗舰产品 Heeyo 是儿童教育 AI 老师产品。Heeyo 目前已获 OpenAI 等 350 万美元投资,最新估值 2000 万美元。 职位描述: 这是一份全职远程的 AI 工程师岗位。AI 工程师将负责模式识别、计算机科学、神经网络、软件开发和自然语言处理( NLP )等任务。他们将致力于开发和实施 AI 算法,设计和培训神经网络,并创建软件解决方案。AI 工程师将与跨职能团队合作,将 AI 技术整合到各种应用和系统中。 工作要求: 跟踪并探索前沿 AIGC/Chat-GPT/LLM 算法和技术,基于 AIGC 算法技术进行教育产品探索; 负责教育领域 AI 技术的研发和落地应用,进行 AIGC 方向的架构设计,研发,维护及优化; 与产品,教研部门紧密合作,确保产品的交付质量和速度; 保持对生成式 AI 领域最新发展的了解,并将其应用于我们的产品 资格条件: Base 新加坡(必要条件) 全栈工程师,有很强的动手和工程实践能力,能够快速进行 demo 实验落地; 熟悉 clip 、扩散模型、NeRF 、MVSNet 、Differentiable Rendering 等深度学习算法,并熟练使用 PyTorch/TensorFlow 等框架,以及图像开源工具库等; 精通 Python 善于沟通,良好的团队合作精神,良好的责任感和进取精神; 有独立分析解决问题能力,良好的文档编写和团队沟通能力。 加分项: 开源项目爱好者,有 github 上开源项目维护经历 创业经历,有过 0-1 初创团队经历 较强编程能力,曾获 ACM-ICPC/CCPC 区域赛金牌以及以上 请提交简历至微信 cocoonbabe
  16. 1 ,可以互传剪切板 2 ,可以互传文件
  17. 相信有很多朋友的孩子学校用钉钉的, 群里会有一些资料需要打印, 每次需要先下载再使用其他 App 打开进行打印, 操作比较繁琐, 有没有办法直接让家里的打印机支持钉钉的云打印?
  18. 主路由是华硕的 ax88u ,桥接模式用来拨号,开通了 IPv6. Apple TV 使用 Qx 作为网关,家里设备多,不想每台设备单独设置网关和 DNS ,所以在路由器上统一设置了 IPv4 网关和 DNS 指向 Apple TV 的固定 IP 。 目前发现一个问题,路由器开启 IPv6 的情况下,家里设备除 Apple TV 本身外都无法连接外网。关闭 IPv6 后所有设备可以正常访问外网,基本可以确定是 IPv6 导致的问题,我猜测可能是路由器只设置了 IPv4 的网关和 DNS ,但家里设备都被分配了 IPv6 的地址,导致仍然通过主路由的 IPv6 DNS 去做解析。但由于家里还在使用 iptv 看电视,所以也无法关闭 IPv6 。 目前我想到的办法是获取并固定 Apple TV 的 IPv6 地址,将主路由的 IPv6 DNS 指向它。但在 Apple TV 中没有找到相关的操作办法。 求教有没有其他方式可以解决呢?梅林固件上的 clash 调整策略太不方便了。
  19. 相较于每天抽时间看书, 我更好奇这些大佬管理这么大的公司,没有烦心事,能够年复一年的保持长期阅读的心情?
  20. 难不成是用 新手账号送的免费额度,堆的账号池?
  21. 开发一个申请免费 SSL 证书的网站, 虽然有 acme 脚本申请,但那对于一些不太懂的人就不那么直观, 也有很多网站提供申请,但无一例外都要注册账号,甚至是手机号, 我嫌麻烦,要像几年前的 SSLForFree 不用注册就能使用就好了, 于是就亲自捣鼓开发一个,对接的免费 SSL 品牌有:Google 、ZeroSSL 、Let's Encrypt , 支持通配域名、IDN 域名(中文域名),并同时生成 pem 和 pfx 两种格式的证书。 欢迎使用和反馈: 草啦 SSL https://cao.la
  22. 突然从电脑桌左手边的抽屉里翻出来了这个玩意 还是我两年前入职 Infosys 送的入职礼品 现在是可算是 come in handy(派上用场)了
  23. 因为我的 MacBook 只是放在房间自己用所以昨天清除了锁屏密码。 结果今天重启后发现自己的 shell 返璞归真了,连变色都没了 brew 也没了🥹 为什么删密码会把这些数据也删了呀。。。
  24. 我艹,太恐怖了 刚刚看了一个私活报价,108 个后台管理系统页面,12000 并且不是简单的 CRUD ,兄弟们,谁把价格压这么低的???? 并且还是简单的原型,需求也很粗糙,这怎么敢的???? 还要赶时间,我靠!!!!
×
×
  • 创建新的...