分类 工作相关 下的文章

公司的Web项目,处理图片延迟加载时,遇到一个图片预留占位的显示问题。记录一下相关解决方案。

1. 需求

本来要解决的问题是,图片“延迟加载”。然后图片未加载之前,需要预留占位,避免加载后撑大页面(主要是为了更好看吧)。

2. 解决方案

图片延迟加载,使用<img>loading="lazy"即可。现代浏览器,当前对其支持还不错。另外,最好不用使用CSS的background加载图片,因为没有很好(或者说简单)的延迟加载解决方案。

对于图片预留占位,主要根据界面设计的布局,选择不同的处理方法。比较麻烦的是,需要自适应浏览器窗口大小的情况。如下:

  • 已知图片宽高。可以随便整。
  • 图片框固定大小。使用CSS的object-fit获得最佳显示效果。
  • 固定列数的图片框自适应浏览器窗口。按浏览器窗口自动计算图片框的宽度。
  • 图片框宽度自适应。使用动态的正方形图片框。这是最终采用的方案。

2.1. 已知图片宽高

可以设置图片按比例缩放,或者按最长边等比例缩放。这个不用说了。

2.2. 图片框固定大小

例如图片框固定,宽为300px,高为300px。图片保持比例,并完整显示,使用CSS的object-fit:contain;。代码如下:

<div style="width:300px; height:300px;">
  <img src="..." style="width:100%; height:100%; object-fit:contain;" />
</div>

2.3. 固定列数的图片框自适应浏览器窗口

一般是列表中带图片的情况,可以使用CSS的单位vwvh

  • vw,浏览器窗口宽度的1%
  • vh,浏览器窗口高度的1%

关于CSS的长度单位,详见: - CSS:层叠样式表 | MDN

缺点:

  • 如果图片的宽高,不能根据屏幕的宽高计算出来,此方案不适用。
  • 如果图片宽高不能适应图片框的比例,也是使用object-fit:contain;按比例缩放。

以下示例,按4列显示,图片框是宽高都为窗口宽度24%的正方形:

<style>
body{margin:0; padding:0;}
.ItemList{width:100%; margin:0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; justify-content: space-evenly;}
.ItemWrap{width:24%; margin:1vw 0 0; padding:0; display: block;}
.ItemWrap img{width:24vw; height:24vw; object-fit: contain;}
</style>
<ul class="ItemList">
  <li class="ItemWrap"><img src="..." loading="lazy" /></li>
  <li class="ItemWrap"><img src="..." loading="lazy" /></li>
  <li class="ItemWrap"><img src="..." loading="lazy" /></li>
  <li class="ItemWrap"><img src="..." loading="lazy" /></li>
  <li class="ItemWrap"><img src="..." loading="lazy" /></li>
  <li class="ItemWrap"><img src="..." loading="lazy" /></li>
  <li class="ItemWrap"><img src="..." loading="lazy" /></li>
  <li class="ItemWrap"><img src="..." loading="lazy" /></li>
</ul>

2.4. 图片框宽度自适应

动态宽高的正方形图片框,是比较折中和灵活的方案。可以不用知道图片宽高。图片框宽度动态计算,高度设为跟高度一致。示例如下:

<style>
body{margin:0; padding:0;}
.ItemList{width:100%; margin:0; padding:0; list-style:none; display:flex; flex-wrap:wrap; justify-content:space-evenly;}
.ItemWrap{width:24%; margin:1vw 0 0; padding:0; display:block; background-color:#c5a29c;}
.ImgWrap{width:100%; height:0; padding-bottom:100%; overflow:hidden; display:block; position:relative;}
.ImgWrap img{width:100%; height:100%; object-fit:contain; position: absolute;}
</style>
<ul class="ItemList">
  <li class="ItemWrap"><a class="ImgWrap"><img src="" loading="lazy"></a></li>
  <li class="ItemWrap"><a class="ImgWrap"><img src="" loading="lazy"></a></li>
  <li class="ItemWrap"><a class="ImgWrap"><img src="" loading="lazy"></a></li>
  <li class="ItemWrap"><a class="ImgWrap"><img src="" loading="lazy"></a></li>
  <li class="ItemWrap"><a class="ImgWrap"><img src="" loading="lazy"></a></li>
  <li class="ItemWrap"><a class="ImgWrap"><img src="" loading="lazy"></a></li>
  <li class="ItemWrap"><a class="ImgWrap"><img src="" loading="lazy"></a></li>
  <li class="ItemWrap"><a class="ImgWrap"><img src="" loading="lazy"></a></li>
</ul>

解析:

  • ul标签,使用灵活的flex样式显示列表。
  • li标签,决定列数。
  • a标签,作为图片框,限制图片显示的宽高。
  • a标签的height:0; padding-bottom:100%;,通过padding-bottom设置高度与宽度一致,是这个方案最巧妙的地方
  • a标签的overflow:hidden;,避免内嵌的图片溢出。
  • a标签的position:relative;,把宽高传给子级。
  • img标签,通过position: absolute;悬浮显示在图片框上面,其宽高获取了图片框(即父级)的宽高。

3. 后续问题

图片加载时,小图最好加载对应的缩略图。体积小,加载快,体验好。但是要考虑图片框大小,避免小图被拉大而导致模糊,降低用户观感。

1. 概述

由于历史原因,工作上,需要把Microsoft SQL Server指定数据库上定时更新的数据,同步到MySQL 8,大概一周一次。

解决方案有几个:

  • 1)采用现成的工具。

    • 但是,一时间没找到好的工具。
  • 2) 开发个程序,读取SQL Server的相关数据,再插入到MySQL。

    • 需要时间开发,且功能上具有针对性。
    • 作为长期使用的工具,这是最优的方案。
  • 3) 从SQL Server生成MySQL的insert语句,再到MySQL上执行。

    • 很多数据库管理工具都提供了数据迁移功能,例如:MySQL Workbench。
    • 如果数据结构不变,insert语句是稳定的,这方案也不错。
    • 要注意采用批量插入,提高导入性能。
  • 4) 从SQL Server导出格式化数据,例如CSV文件,再导入到MySQL。

    • 由于保存数据的CSV文件比较通用,相关的数据库管理工具都支持导入导出。
    • 但是要注意要处理数据格式、NULL数据等问题。

目前采用了第3个方案,CSV文件比较通用,也不用考虑怎么开发。但是偶尔会出现导入MySQL失败的问题,比如出现了NULL数据。后面应该会写个程序处理,直接生成insert语句。

2. SQL Server导入导出CSV

一般使用BCP命令。即Bulk Copy Program,是一个命令行工具,用于在SQL Server之间批量传输数据。由于微软推出了SQL Server for Linux,所以可以完全在Linux执行导入导出的操作。另外,微软提供了SQL Server for Linux的官方Docker镜像,比Windows上安装SQL Server Express更方便,非常适合开发测试使用(主要应付历史)。

官方介绍及参考文档如下:

2.1. BCP使用说明

直接运行bcp命令,会提示其用,如下:

> C:\Program Files (x86)\Microsoft SQL Server\Client SDK\ODBC\130\Tools\Binn\bcp.exe

用法: bcp {dbtable | query} {in | out | queryout | format} 数据文件
  [-m 最大错误数]             [-f 格式化文件]       [-e 错误文件]
  [-F 首行]                   [-L 末行]             [-b 批大小]
  [-n 本机类型]               [-c 字符类型]         [-w 宽字符类型]
  [-N 将非文本保持为本机类型] [-V 文件格式版本]     [-q 带引号的标识符]
  [-C 代码页说明符]           [-t 字段终止符]       [-r 行终止符]
  [-i 输入文件]               [-o 输出文件]         [-a 数据包大小]
  [-S 服务器名称]             [-U 用户名]           [-P 密码]
  [-T 可信连接]               [-v 版本]             [-R 允许使用区域设置]
  [-k 保留 Null 值]           [-E 保留标识值]
  [-h"加载提示"]              [-x 生成 xml 格式化文件]
  [-d 数据库名称]

2.2. BCP导出CSV文件

CMD批处理命令参考如下:

set CUR_PATH=%~dp0
set BCP="C:\Program Files (x86)\Microsoft SQL Server\Client SDK\ODBC\130\Tools\Binn\bcp.exe"
set BCP_PARAM=-S "IP,端口" -U "用户名" -P "密码" -d "数据库名" -t \t -b 1000 -c -C 65001 -k

rem 导出指定数据库表的数据
%BCP% 数据库名.dbo.表名 out %CUR_PATH%table_export.csv %BCP_PARAM%

rem 导出指定查询语句的数据
%BCP% "select查询语句" queryout %CUR_PATH%query_export.csv %BCP_PARAM%

参数说明:

  • -S 服务器名称,服务器IP与端口之间,使用英文逗号(即“,”)分隔。
  • -t 字段终止符,默认是Tab符号(即“/t”)。
  • -b 批大小,如果导出数据太多,需要分页操作,默认是1000。
  • -c 字符类型,设置导出文件的字符编码为UTF-8时,设置为“-c -C 65001”,要注意大小写。

要注意,导出的CSV文件不带字段名称。需要记录字段名称时,目前只能把字段名称插入到CSV文件的第一行,并且以数据行的分隔符号进行分隔。

2.3. BCP导入CSV文件

要先创建对应的表,才能执行导入。格式如下:

set BCP_PARAM=-S "IP,端口" -U "用户名" -P "密码" -d "数据库名" -t \t -b 1000 -c -C 65001 -k
bcp 数据库名.dbo.表名 in 数据文件.csv %BCP_PARAM%

2.4. SQL语句执行BCP命令

要注意,用户需要授权可执行xp_cmdshell的权限。官方详细说明如下:xp_cmdshell (Transact-SQL) - SQL Server | Microsoft Learn

格式如下:

exec master..xp_cmdshell 'bcp ...'

2.5. SQL Server的其它导入导出方案

3. MySQL导入导出CSV

3.1. 相关参考

参考文章:MySQL导出数据为CSV的方法

MySQL官方文档:

3.2. MySQL导入CSV

假如导入数据到数据表user_table,该表有字段id、name、remark,其中remark数据可能为NULL。

-- 导入CSV文件的SQL语句
load data infile '/var/lib/mysql-files/import_data.csv' into table user_table fields terminated by '\t' escaped by '' optionally enclosed by '"' lines terminated by '\n' ignore 1 lines (id,name,@remark) set remark=nullif(@remark,'');

说明:

  • CSV文件需要放在mysql用户有权限的目录,比如/var/lib/mysql-files/
  • fields terminated by '\t',表示CSV数据以TAB符号分隔。
  • escaped by '',设置转义字符,默认的是反斜杠(backslash:\ ),设置空值('')表示不适用转义。
  • optionally enclosed by '"',以双引号包裹单一字段的数据。
  • lines terminated by '\n',每行数据的结束符号。
  • ignore 1 lines,导入数据时跳过第一行,因为第一行是字段名称的说明。
  • (id,name,@remark),把一行数据关联到对应的字段。其中@remark是把数据赋值到变量,后面有特殊处理。
  • remark=nullif(@remark,''),表示remark字段的数据,根据@remark变量进行处理。这里是NULL数据转为空字符串。

3.3. MySQL导出CSV

-- 导出CSV文件的SQL语句
select * from user_table into outfile '/tmp/expor_data.csv' fields terminated by '\t' escaped by '\\'  optionally enclosed by '"' lines terminated by '\n' ;

说明:

  • fields terminated by "\t",表示CSV数据以TAB符号分隔。
  • escaped by '\\',设置转义字符,默认的是反斜杠(backslash:\ ),设置空值('')表示不适用转义。
  • optionally enclosed by '"',以双引号包裹单一字段的数据。
  • lines terminated by '\n',每行数据的结束符号。

4. SQL语言的concat_ws函数

SQL的select语句可以使用concat_ws函数,可以实现一行数据的所有字段值合并成一个字符串,并指定分隔符号。然后把查询结果保存为文本文件(包括CSV),即实现了数据导出。参考文档如下:

Windows 11的任务栏,确实很鸡肋。于是想办法做功能增强。

1. 存在问题

对比历史版本Windows(XP、7、10)的任务栏,Windows 11任务栏存在以下缺点:

  • 可自定义功能不多
  • 不支持任务平铺,即不进行分组合并显示(注,2023-11-20,发现Win11更新后可以设置任务栏图标不合并)
  • 不能添加快捷菜单集合
  • 不能调整高度,占据屏幕空间比较多

2. 可选方案

2.1. ExplorerPatcher

2.2. StartAllBack

2.3. CLaunch

  • 项目地址:CLaunch
  • 优点:免费,轻量,对系统没有任何影响。
  • 缺点:只是个弹出菜单(不能替代任务栏),不开源。

3. 解决方案

最后,我选择了基于CLaunch,实现任务栏辅助。主要是解耦,避免对系统的影响,也便于维护、迁移和备份。

3.1. 任务栏设置

任务栏设置为自动隐藏,节省屏幕空间。

注:由于任务栏可以设置图标不合并,没必要隐藏任务栏了。

3.2. 部署CLaunch

其当前最新版v4.05,由于在virustotal.com被大量杀毒软件检测出木马,所以选择了v4.04。

皮肤推荐:yurafuca/claunch-win10: Windows 10 Flavored CLaunch Skin.

我比较喜欢只有一列的布局(像《刀剑神域》游戏里的菜单),并且设置显示规则为:

  • 双击屏幕左侧边缘显示
  • 双击桌面显示
  • 快捷键 Ctrl + Shift + Q 显示
  • 任务栏添加固定图标,点击即可显示

3.3. 常用快捷方式

常用的快捷方式,会保存到指定文件夹,在CLaunch指向该文件夹,并以子菜单显示。这样维护、备份、迁移都比较简单。

3.4. 平铺显示任务列表

Windows 11自动一个平铺任务列表,按 Win + Tab 显示。可以编写vbs文件,并在Claunch设置为按钮,现实点击显示。相关代码如下:

rem 文件名:tasks.vbs
rem 运行平铺显示的任务列表,Windows默认快捷键:Win + Tab
rem 参考:https://learn.microsoft.com/en-us/windows/win32/shell/shell-windowswitcher
set objShell = CreateObject("shell.application")
objShell.WindowSwitcher
set objShell = nothing

3.5. 添加快捷功能

比如要在Claunch添加按钮,实现显示桌面,可以使用vbs模拟按键实现。相关代码如下:

rem 文件名:desktop.vbs
rem 显示桌面,Windows默认快捷键:Win + D
rem 参考:https://learn.microsoft.com/en-us/windows/win32/shell/shell-toggledesktop
set objShell = CreateObject("shell.application")
objShell.ToggleDesktop
set objShell = nothing

关于vbs模拟按键,可以参考:VBS自动按键大全,vbs基本和特殊按键 - 笨蛋敏 - 博客园

例如要实现显示Windows开始菜单,可以使用Ctrl + ESC,模拟Win按键。相关代码如下:

rem 文件名:start.vbs
rem 显示开始菜单,Windows默认快捷键:Win
Set wsShell = WScript.CreateObject("WScript.Shell")
wsShell.SendKeys "^{ESC}"
set wsShell = nothing

3.6. 通讯软件的消息提醒

通讯软件有消息时,任务栏有提醒效果。如果任务栏自动隐藏,收到消息时会自动显示。如果觉得还不够,可能会导致错过消息,可以设置通讯软件(例如TIM),收到新消息就弹出对话窗口。

3.7. 显示时间

目前没有很好的解决方案。要么就装个显示时间的软件(例如 DesktopClock),要么就弹窗显示当前时间(代码如下)。但是两个方案都不够好用。

rem 文件名:show_time.vbs
rem 弹出当前日期时间
rem 注意:本VBScript文件涉及中文显示,需要使用ANSI编码
rem
rem 参考:
rem https://learn.microsoft.com/en-us/windows/win32/shell/shell-windows

rem 弹窗标题
title = "当前时间"
rem 弹窗保持的秒数,指定时间之后自动关闭。0为不自动关闭。
holdSec = 3

curDateTime = Now()
weekdayInt = Weekday(curDateTime)
Select Case weekdayInt
  Case 1
    weekdayStr="星期日"
  Case 2
    weekdayStr="星期一"
  Case 3
    weekdayStr="星期二"
  Case 4
    weekdayStr="星期三"
  Case 5
    weekdayStr="星期四"
  Case 6
    weekdayStr="星期五"
  Case 7
    weekdayStr="星期六"
  Case Else
    weekdayStr="星期日"
End Select

msg = Year(curDateTime) & "-" & Right("0" & Month(curDateTime), 2) & "-" & Right("0" & Day(curDateTime), 2) 
msg = msg & vbCrLf & Right("0" & Hour(curDateTime), 2) & ":" & Right("0" & Minute(curDateTime), 2) & ":" & Right("0" & Second(curDateTime), 2)
msg = msg & vbCrLf & weekdayStr

rem CreateObject("Wscript.Shell").Popup msg, 3, title, 64
Set objShell = CreateObject("WScript.Shell")
objShell.Popup msg, holdSec, title, 64
Set objShell = nothing

1 背景

受“新冠肺炎疫情”影响,出现不能回办公室上班的问题,所以制定一套安全的远程办公方案。

由于办公室有外网IP,原来的方案就是利用路由器的端口映射功能,把各个台式机(操作系统是Windows)的“远程桌面”端口直接映射到外网。这方案缺点如下:

  • “远程桌面”如果存在漏洞,比如绕过登录,电脑上的资料就可能被随意访问。
  • 需要远程访问的台式机,要24小时开机,否则连不上。
  • 需要远程访问的台式机,起码占用路由器的一个端口。

2 解决方案

结合SSH服务、wake on lan、远程桌面,实现更安全和灵活的远程办公。

  • 部署Linux服务器,只映射其SSH服务端口到外网,作为安全入口。
  • SSH客户端几乎覆盖所有平台(包括移动平台),且其功能强大。
  • 使用SSH的端口转发(Port Forward)功能,连上办公室内网的指定IP的“远程桌面”端口。
  • 各个台式机开启wake on lan功能,实现按需开机,工作完关机。
  • 各种操作系统有对应的远程客户端。Windows,使用微软的“远程桌面”客户端,全平台支持;Linux,使用SSH客户端;Mac操作系统,使用VNC客户端。

但是此方案仍有缺点:

  • 需要用户理解SSH及其功能。
  • 使用Linux远程开机命令(wakeonlan),即使把命令简化为Shell脚本,也不是普通人会用。
  • M系列CPU的Mac电脑,不能使用wake on lan,目前只能长期开机。

3 办公室部署

3.1 路由器

路由器的网络需要可外网访问,并且支持端口映射功能。基本路由器都支持端口映射,具体配置参考路由器说明书。

配置路由器外网端口,映射到Linux服务器的SSH服务端口。

3.2 Linux服务器

  1. 安装wakeonlan命令。

Debian或Ubuntu,执行以下命令安装

sudo apt install wakeonlan
  1. 部署SSH服务,作为安全入口。需要SSH服务的安全配置,例如:
  • 仅使用SSHv2协议

    Protocol 2
  • 禁止root用户登录。

    PermitRootLogin no
  • 禁止用户空密码登录。

    PermitEmptyPasswords no
  • 指定白名单用户。

    AllowUsers user1 user2 user3
  • 指定禁止登录的用户(一般指定白名单即可)。

    DenyUsers root user4 user5
  • 限制身份验证最大重试次数。

    MaxAuthTries 3
  • 登录用户的密码,使用强密码,甚至配置使用“密钥”验证登录。
  • 显示最后一次登录的日期和时间。

    PrintLastLog yes
  • 防止特权升级(一般默认配置)

    UsePrivilegeSeparation sandbox
  • 禁用 GSSAPI 认证

    GSSAPIAuthentication no

更详细的设置,可以搜索“Secure SSH”或者“SSH安全加固”等内容。

另外,最好配置一下服务器保持TCP连接的选项,避免客户端自动断开:

  • 开启保持TCP连接

    TCPKeepAlive yes
  • 向客户端发送是否存活的消息的时间间隔,单位是秒,默认是0,不发送

    ClientAliveInterval 30
  • 请求后客户端无响应则自动断开的最大次数

    ClientAliveCountMax 3

3.3 台式机

  1. 主板开启wake on lan功能。具体BIOS设置,需要查询主板的说明书。一般注意以下几点:

    • 板载有线网卡设置启用。
    • wake on lan设置启用。
    • 启动项,允许PCIE设备启动。
    • 启动项,出现pxe rom可选。
  2. 操作系统开启wake on lan功能。即操作系统执行关机时,让主板不要完全断电,并允许网卡运行于可接收Magic Package的状态,用于网络启动电脑。

  3. 开启远程访问服务。各个操作系统配置如下:

    • windows,开启“远程桌面”服务。
    • Linux,开启SSH服务。一般默认开启的。
    • Mac OS,开启“远程访问”服务,可以SSH客户端访问,即字符界面。
    • Mac OS,开启“远程桌面”服务,可以VNC客户端访问,即图形界面。

4 客户端部署

主要就是SSH客户端 + 远程客户端。

4.1 SSH客户端

4.1.1 Linux

一般Linux操作系统默认安装SSH客户端,如果没有,安装“OpenSSH”或者“Dropbear SSH”的客户端即可。

4.1.2 Windows

Windows 10或11可以通过“WinGet”命令安装“OpenSSH”客户端。例如:

winget install opensssh

Windows 7可以使用“PuTTY”。Windows都可以安装这个。

4.1.3 Android

可以使用“Termux”,再安装“OpenSSH”。

pkg install openssh

或者使用其它SSH客户端App。

4.1.4 iOS

安装Termius。需要注册账号,免费版可以使用SSH客户端和端口转发功能。

4.2 远程桌面客户端

  • Windows,自带“远程桌面”客户端。
  • Linux,推荐安装“Remmina”。
  • Android,安装微软官方“远程桌面”App。
  • iOS,安装微软官方“远程桌面”App。

4.3 VNC客户端

  • Windows,使用开源的“TightVNC”。
  • 其它,待补充。

5 客户端使用

以Windows远程桌面为例,其默认端口为3389,并假设该台式机的IP为192.168.0.123。其它服务类似操作。

  1. 远程开机。

    启动SSH客户端并登录,使用wakeonlan命令 + MAC地址,启动对应的台式机。注意,需要记录该台式机有线网卡的MAC地址。
  2. 开启端口转发。

    启动SSH客户端,设置本地端口(例如 43389)转发到办公室内网指定电脑端口(例如 192.168.0.123:3389)。
  3. 连接远程桌面。

    远程桌面客户端连接到本机端口(例如 127.0.0.1:43389),即可访问。如果是管理员帐号登录,需勾选“管理员模式”。

5.2 远程开机

普通用户执行wakeonlan命令,参数是对应台式机网卡的MAC地址。然后使用ping命令,检查该台式机是否开机成功。

要注意,Windows操作系统,不要使用shutdown /s命令关机,会导致wakeonlan命令无法开机。

5.3 开启端口转发

假设,办公室的外网域名为remote.office.com,SSH映射外网端口为22222,SSH登录用户为r-user,需要通过访问192.168.0.123:3389的“远程桌面”服务,并且本机开启43389端口去访问。

5.3.1 SSH命令

使用SSH客户端(例如OpenSSH客户端)的,直接执行以下命令,然后输入密码,让其一直运行即可。

ssh -f -N -L 43389:192.168.0.123:3389 r-user@remote.office.com -p 22222 -o ServerAliveInterval=30

关键参数说明如下:

  • -f后台运行。
  • -N不执行命令。
  • -L 43389:192.168.0.123:3389是把本机43389端口转发到办公室内网的192.168.0.123:3389端口。
  • -o ServerAliveInterval=30是每30秒向服务器发生一条表示客户端存活的消息,用于保持连接。

关于客户端保持连接,可以修改/etc/ssh/ssh_config文件,在Host *的配置下,加入以下配置。然后运行ssh命令,不用加上-o ServerAliveInterval=30这个参数。

ServerAliveInterval 30
ServerAliveCountMax 3

5.3.2 PuTTY设置

  1. 点Category -> Session,在Host name填remote.office.com,Port填22222,Connection Type选SSH。
  2. 点Category -> Connection -> Data,在Auto-login username填r-user
  3. 点Category -> Connection -> SSH -> Tunnels,Add new forward port下,Source port填43389,Destination填192.168.0.123:3389,勾选下面的“Local”和“Auto”,再点“Add”。
  4. 点Category -> Connection,在Seconds between keepalives (0 to turn off)填10,并勾选Enable TCP keepalives (SO_KEEPALIVE option)选项。这一步是设置客户端保持连接。
  5. 点Category -> Session,在Saved Sessions填remote_office,再点“Save”保存配置。
  6. 连接时,点Category -> Session,选中remote_office,点“Open”。输入密码后让其保持运行即可。

5.3.3 iOS设置Termius

  1. 安装Termius,并注册账户。
  2. 设置保持后台运行。

    • 在Settings -> SESSIONS -> 开启”Active Connect Saver“和”Save Location Data“。
    • 据说是使用了“获取地理位置”权限,实现App保持后台运行。
  3. 新建Hosts。

    • 填写连接到办公室的域名remote.office.com和SSH端口22222,然后命名为remote_office
  4. 新建Port Forwarding。

    • 在Port Forwarding,点“+”新建。
    • -> 选Local,点Continue。
    • ->“Set the local port and binding address”的Port number填写映射到本机的端口,例如3389,点CONTINUE。
    • -> 点Select a host,并选sdoffice。
    • -> “Set the destination host”填写目标电脑的内网IP和远程桌面端口,例如address为192.168.0.123,port为3389,点CONTINUE。
    • -> 最后填写标签,例如101-rdp,点DONE
  5. 连接。

    • 在Port Forwarding,长按101-rdp,点Connect。

5.4 远程桌面客户端

添加电脑,电脑名称为127.0.0.1:43389。如果是使用管理员账号,记得开启“管理员模式”。

6 其它方案

6.1 前端安全替代

  1. 使用虚拟内网,即VPN。连上VPN就等于进入办公室内网。

    • Android和iOS原生支持L2TP、IPSec、IKEv2等协议,不用安装客户端。
    • 路由器同样只需映射VPN服务的端口。
  2. 使用堡垒机做入口。

    • JumpServer。未了解。
    • Next Terminal。了解过,当前版本安全方面考虑不足,手机访问“远程桌面”不支持触屏等。
  3. 其它商业解决方案

    • TeamViewer
    • 向日葵远程控制软件

6.2 网络启动功能替代

可以使用WiFi开关 + 电脑通电启动,实现替代,但需要购买WiFi开关硬件。

终于在5月来临前结束了上一份工作。回望过去,刚好满一年。

一年前,眼前一切都安好了,想再次投入到软件公司去做开发。面试多间以后,最后选择了本地数一数二的,而且声称做Java的公司。现在想来,进去也太过于轻松了。

首先是HR招人会有奖金,其次是公司的经营方式--可以理解为人力外包。就是找一些会写代码的程序员,拉到客户那边帮客户做开发。公司赚取客户报价与员工工资之间的差价。这种开发模式,除非客户那边有比较强的技术,并且只需要增加临时的开发人员来完成项目,否则对客户是非常不利的。最简单一点是项目开发过后,就找不到人去维护。实际项目中,也有出现某些代码想修正或者升级,问谁,谁都不知道。

基于这种经营方式,就会导致人员流动性很大。当然,公司只要从客户那边拿到钱,就不用管人员的问题了。更厉害的是,开发工具采用了图形化开发。就是像配置工作流一样去配置业务逻辑。简单直观,开发快,而且不公开技术实现。新人立马就能上手,就算离职也不会学到真正的技术。

这样来看,一个声称是做技术的公司,其实就没有什么核心技术了。进去工作的程序员,也得不到太多的提升。再加上利用晚上不算工资、没有补贴的加班来压缩成本,人就会越做越累。正如某领导所说的,能留下来的都是精英。我的理解,他们都是心态非常好,抗压能力超强的精英。

当然,老板和领导们都应该看到这些问题了(或者看到其它方面),于是踏上了产品研发的道路。我一进去就是投入到新产品的研发,一做就是大半年。然后我们拿着这个半成品,就去给客户做项目开发了。

现在看来,该产品也一样空洞。主要的原因还是缺乏一个技术总监去主导、把控和验收。最后随着产品开发的结束,开发人员的离去(调去其它项目或者离职),产品未能达到预期效果。最TMD的是,有一些东拼西凑的模块,隐含重大问题。例如在Windows上部署Weblogic来运行,核心Jar包会报包名与类名冲突的问题。这样连部署到客户的服务器都成为难题。

啰嗦了一堆问题后,还是总结一下收获吧。

重新投入到软件开发的环境中,感觉还是很好的。一个开发团队里,有问题可以大家一起讨论解决。下班后还能天马行空地讨论各种各样的问题。

不得不说的是,技术老大传授了一些不错的经验。

对于技术的选择,要用自己熟悉的,而不是选择那些吹嘘有多强的新方案。自己熟悉的,遇到问题都好解决。而且成熟稳定的方案会少很多问题,就算有问题,解决方法也比新方案容易找到。

要通过增强代码可复用性来提高开发效率,而不是复制修改代码。其实这个程序员都明白的,但是在开发过程中总是没有做到。

针对问题去解决问题。就是根据问题的提示去找原因,然后解决。

面试的时候,要把话题带到自己熟悉的领域,说出面试官不懂的、不敢反驳的话题。

展望未来,将有更多需要努力和学习的地方。Come on, go ahead!

由于现在的公司有多个老旧系统,需要从一个系统访问到另一个系统的数据。一看到这个,就想到了SOA,但是几个小系统部署个SOA来整合,值得么?重点是没搞过SOA!于是怎么简单就怎么解决,例如实现跨数据库访问,即从SQL Server访问MySQL,程序层就从SQL Server读取数据。

Google了一下,找到一篇有用的教程:SqlServer2005 链接服务器用法(http://www.jb51.net/article/18484.htm)。文中提到用sp_addlinkedserver这个存储过程来添加链接服务器(要先配置好MySQL的ODBC数据源),然后通过以下语句获取数据:

SELECT * FROM OPENQUERY (链接服务器名,'MySql查询语句');

按照这个方法配置好,并建立了视图,然后就可以进行查询了。但是有很多问题:例如SQL Server不支持MySQL的某些数据类型,导致查询结果出错,还有个致命的问题,查询速度很慢。

最后还是写了个ASP页面,直接访问MySQL数据库,返回查询结果。打印时,也是写个ASP页面来实现。这,比什么都简单。汗……

PS. 后来还发现如果是SQL Server之间的访问,可以直接用OpenRowSet函数来实现跨数据库访问。格式如下:

OPENROWSET ( 'provider_name', { 'datasource' ; 'user_id' ; 'password' | 'provider_string' }, { [ catalog.] [ schema.] object | 'query' } )

太方便了,特别是两个数据库之间复制数据,或者进行数据对比。

中午12点多,被组长送上火车后,就踏上这个既长又寂寞的旅程--坐上回广州的火车。24个小时以上的旅程,以陈绮贞那《旅行的意义》开始,散发淡淡的忧伤。陈老师的歌实在太棒了!特别适合这种悠长的旅程。

天津的出差总结?很想写,但又懒洋洋的,不想马上写。管它呢,旅程还长着。先吃点面包填下肚子。

PS. 今天是日本在二战投降65周年,也是舟曲特大泥石流灾难全国哀悼日。

整个7月份在天津出差,虽然去了几天首都北京,但也没一天去玩过。其实分给我的工作不多,只是一面对电脑就没精神了,导致每天都在混日子似的。

首先是模块要做成怎样,没跟软件经理(他设计了系统)沟通清楚,很多地方不敢下手。而软件经理根据另外一个项目的经验,把一些模块都搬过来让我改。其实只做修改的话,应该比重新开发更快,但是界面做得比较复杂。主要是设计了一大堆JavaScript,而这堆JavaScript都保存在js文件里,调试很麻烦。

其次是客户的需求,跟系统设计前调研的不同。由于客户是个国企类企业,花国家的钱(就是纳税人的钱)不心痛,内部管理又混乱(一些流程、文件没定下来),需求变化是注定的了。还有属下有十几个子公司,任何涉及到子公司的流程都要做十几个。这给开发部署带来了巨大的压力。

工作两年了,深深感到信息管理系统也不容易。说来说去,主要是人的问题。还有就是系统不够灵活,这应该算是技术问题吧。

PS.  1)跟同事聊天才知道,公司居然有个报表系统。这系统就是录入客户数据,然后出报表。进一步的价值就是根据录入的数据进行BI(商业智能)。但是由于没有把客户的业务、流程做到系统里,客户使用系统就会比原来增加工作量。如果业务和流程都做到系统(就是一般的信息管理系统),客户就可以真正的把系统用起来,使企业真正实行信息化。而这报表系统由于见效快,所以容易骗到客户的钱,但这样对客户极度不负责任!扯淡!

2)终于跟天津驻守的占起哥见面了,还吃了顿饭。在占起哥强烈要求之下,来到一家有点档次的海鲜酒楼吃饭。点菜是看着模型点,做出来的菜跟模型几乎一样。这顿饭除了能在毕业两年后与占起哥见面之外,还让我第一次吃到了三文鱼和金枪鱼刺身。88RMB一盘,就那么几块肉,感觉还没想象中那么好吃。

3)离开北京前,跟一学弟吃了个饭。吃了几百RMB,就那么三小碟菜,一卑蜂蜜柠檬茶也要28!不过那学弟9K RMB/月,还让我随便点。他以前是公路专业的,毕业时寄了一个Flash作品过去,就被那公司录用了,去做Flash游戏。工资在一年里从3K涨到9K,真让人羡慕。而且老板是个老外,工作之余还锻炼了英语口语。

由于公司参与某大项目的投标,需要做个系统的界面原型,而我刚好在天津,于是被召去北京了。本来随行的还有个组长,但他病得不行了,只能我一个人过去。幸好有北京分公司的同事带路,一切都顺利进行。

现在坐上天津到北京的动车了。第一次坐动车很舒服,还有带有第一次坐地铁的兴奋感。坐在车上,听着Dido,很小资~

到北京后希望能去向往已久的中关村逛逛,附近还有清华大学和圆明园遗址,也值得看看。不过玩之前得完成任务--想到要面对着领导工作,真不习惯。希望一切顺利吧。

先睡个午觉,估计醒来后就到了。

PS. 我还没睡醒,带路的MM就说到了。

坐在火车窗边,望着窗外远去的风景,听着《旅行的意义》,蒙蒙睡意还没褪去,淡淡的忧伤油然而生。

昨天跟组长终于出发去天津了。背上大大的爬山包,走向火车站,大学时的回忆涌上心头。计划24小时以上的火车旅程很自然地开始了。昨晚吃过方便面后,聊着QQ,不知不觉地睡着了。应该才八九点左右,算是这今年最早睡的一个晚上。深夜醒来,发现QQ一堆留言,可是手机不能上网了。不管,继续睡去。

今早醒来,懒洋洋地下了床,分别用干湿纸巾擦擦脸,用矿泉水漱漱口,顺便喝掉。唉,午饭依然是方便面。比较有趣的是经过河南时,看到黄河,真的很黄!黄河边上有两个大头像,不知道是什么历史人物。还有经过许昌、郑州、邯郸,幸好能下车,在月台上拍个照。在石家庄那站,本来可以跑到火车头拍个照,但是只有几分钟时间,来不及。

好了,已经从石家庄开车了,估计天津也不远了。

PS. 1)沿途看到的河流不是黄色就是黑色的,铁道两边的垃圾,还有高耸的烟囱,庞大的火力发电站,甚至看到黄色的烟飘出来(估计是带硫的)……究竟人类对自然的破坏有多大,有多深?

2)同学朋友们都问我去哪了(因为QQ签名上写着“再见了珠海,再见了顺德,再见了广东“),只有两个朋友第一句就问去天津多久。

3)幸好出差前邮购了个USB移动电源(据说9600mAh不虚报),给G1带来源源不断的电力,打发着火车上无聊的时光。