惊魂一夜!有福新闻UfqiNews升级到PHP8

有福新闻UfqiNews的老版本依然运行在 GWA2 PHP ( https://ufqi.com/dev/gwa2/ )上,去年底PHP编程语言本身升级到了PHP8, 获得好评,我们即打算择日升级GWA2 PHP到PHP8版本。

考虑到之前关于升级的经验教训,任何开源软件,能够在生产线部署的,一定是当前稳定版本(latest stable release)的上一个版本,也就是上一个稳定版本的最新小版本。所以我们考虑将 有福新闻UfqiNews的GWA2 PHP升级的话,也不会直接去PHP8,而是先使用 PHP8之前的一个稳定版本PHP7.4.

又由于软件开发和使用,尤其是系统软件的开发和使用,另外一个黄金法则是能够运行就尽量不要去做任何修改(包括升级、优化等)。所以如果没有一定的动力的话,我们是不会去修改(升级、优化)一个运行正常的系统。

不幸的是,时过境迁,物换星移,人们对软件系统的改进期望是无止境的。很快我们就遇到一个需要通过升级来解决的问题:当前的UfqiNews ( https://ufqi.com/news/ ) 系统里在处理图片水印时,无法在WebP格式的图片上打水印。这也不是大问题,要求图片另存为其他格式也可以绕过去,毕竟WebP格式的图片还是新事物。可是看到WebP的发展势头,似乎普及起来也只是时间问题,加上对PHP8的神乎其技的“飞速”传言,我们就依仗“艺高人胆大”的脑门一热的开启了在 UfqiNews上升级PHP8的操作了,从而遇上了惊魂一夜,PHP8有不兼容的问题,尤其是看起来像是走退路的NULL值问题和数据类型错误抛出TypeError/ValueError。

PHP Built-in function count() Changelog ¶

VersionDescription
8.0.0count() will now throw TypeError on invalid countable types passed to the value parameter.
7.2.0count() will now yield a warning on invalid countable types passed to the value parameter.

PHP Built-in function explode() Changelog ¶

VersionDescription
8.0.0explode() will now throw ValueError when separator parameter is given an empty string (""). Previously, explode() returned false instead.

另一个受影响较为严重的PHP内置函数是 implode ,
PHP 7及之前,
$str = implode(“-“, $array);
# 能吃屎, 不管 $array 是啥料,都正常返回,继续程序 …
PHP 8及之后,
$str = implode(“-“, $array);
# 开始硬怼了, $array 类型不对时,直接抛出错误,撂挑子不干了!

PHP之所以雄起的“初心”之一就是有种“能吃屎”的精神,一个变量不管啥类型,都能顺利执行跑通,即便结果是非预期的,断然不至于将程序退出来,直接硬怼调用者说,“傻货,你输入的数据有问题!”

通常只有那些强类型的、预编译的语言的才有那么硬刚硬怼的底气,PHP之前是断然不会的。真是由于这种“有事私下说,有问题日志里打小报告”的平易近人,获得了很多小白初级程序员的喜欢,遇上互联网的东风,逐渐成燎原之势。PHP7及之前的程序代码,只要没有形式上错误,基本上都能顺利运行、跑出结果(或可能非预期)。繁琐复杂的编译、类型检查、内存申请与销毁、垃圾回收、数组越界、空指针、Nnll对象等等一些列问题,语言类库及API本身都做了一系列的努力,使之尽可能不终止程序的运行。

这种平易近人其本质是语言本身的类库及API对程序开发者、对程序使用者提供了异常情况处理工具,是某种包容精神的体现。程序员会搞错类型,终端用户也会手抖,网络也不稳定,操作系统也偶尔异常,输入的数据类型有问题似乎在所难免。

语言开发者这么升级改进长脾气地硬怼程序开发者,如果程序开发者不做异常处理,则就一路硬怼到终端用户那里。这显然是不行的,让终端用户吃瘪,程序员是无法交代的,程序员可以往回找语言开发者吗? 纵有理由千千万,简单易行的PHP,不该忘了初心,将这些繁琐的、基本的检查抛出来(而不是兼容处理消除消化掉问题),把强类型语言的“缺点”学来,作为“长进”。

这些背后可能是复杂和漫长的决策、权衡与博弈,其本身是某种方法论的对比,其本身都没有对与错,而是一些取舍和边际选择。这让我想起了我们在设计 GWA2 的API接口时的一些考虑,同时也在阿里巴巴和Google的一些Java语言规范( https://ufqi.com/news/ulongpage.1507.html?tit=Java開發手冊-阿里巴巴-嵩山版-2:編程規約 )中出现过。

比如在 GWA2 的主体方法与接口中,如果方法的返回值是基本类型( primitive scalar types ),应该是有个默认返回值,以上面 PHP 的 count 函数为例,我们预期的写法:
function int count($list){
int c = 0; # default or on failure
if(!isset($list)){ } # a waring
else if($list == null){ } # another warning
else if(!is_array($list)){ } # 3rd warning
else{
# count based on some rule…
c++;
}
return c;
}

如果是非基本数据类型的,如数组、列表或者Hash表、资源描述符等返回值( non-primitive compound types ),则应该是两个返回字段,一个字段标注是否成功处理,另外一个字段则是处理的实际结果值。以上面 PHP 的 explode 函数为例,我们预期的写法:
function array explode($separator, $string){
$rtnArray = array(); $rtnList = array;
$isSucc = false;
if(!isset($string)){ } # a waring
else if($string == null){ } # another warning
else if(!is_string($string)){ } # 3rd warning
else if($string == “”){ } # 4th warning
else{
# split based on some rule…
$rtnList[$i] = function($string, $i);
$isSucc = true;
}
if($isSucc){ $rtnArray[0] = true; $rtnArray[1] = $rtnList; }
else{ $rtnArray[0] = false; $rtnArray[1] = $rtnList; }
return $rtnArray;
}

GWA2 Manual Book
GWA2 Return Values of Methods

附记1.

为了使得系统能够支持WebP格式的图片文件支持水印功能,我们打算升级PHP语言环境。

起初,我们并不打算升级到 PHP8,而是打算升级到PHP7.4这个版本,在实际编译PHP源代码时,当前系统上报错说缺少 oniguruma类库,即便通过yast/zypper 安装了 oniguruma-devel 之后,仍然报错说无法找到,而相同配置的另外其他服务器则能够顺利找到&通过编译。 Linux还有很长的路要走。
既绕无法安装PHP7.4, 那就试试PHP8, 结果还是类似错误,预计只好针对oniguruma这个类库下点功夫,经过一番折腾,终于通过手工下载 https://github.com/kkos/oniguruma 一份源代码,手工编译&安装之后才通过。

解决了 oniguruma 这个问题之后,在编译PHP8的时候,还遇到 ext/geoip 无法编译通过的情况,后来通过手工下载 https://github.com/rlerdorf/geoip 一份重新放入 ext/geoip 才算通过。

尽管解决了那么多问题,具体PHP8兼容性问题,还没有显现。由于担心会出现异常,我们选择在深夜流量较小的时候找一台服务器进行试装,结果就遇上重启之后大面积报错的情况,举凡遇到 count 和 implode /explode等地方,都需要手工增加相应类型或数据安全检查代码。

顺带的昨晚还未被 Hanjst ( https://ufqi.com/dev/hanjst/ ) 替换掉的PHP Smarty模板引擎,一样随着PHP8的升级,触发一团糟的问题,服务被迫终止了一会。庆幸我们在后续的GWA2中将逐渐解耦模板引擎,使用 Hanjst替换掉Smarty,与服务器端开发语言不再绑定。

再一次的惊魂一夜!再次印证了,不要就近升级到软件的最新的latest stable release,惊魂大于惊喜。
再次印证了,能跑的系统,尽量避免升级,各种问题会导致业务被迫中断。智者千虑,必有一失。

—-

附记2.

这是使用 WordPress 的PHP8兼容版,创作的第一篇Blog,WordPress的PHP8的版本升级比预期的快,体验好!值得学习。

WordPress的新版升级过程大致为:
1)备份原程序和资源文件,备份原数据库;
2)检查备份的可还原性;
3)新创建WordPress子目录,或者将现有目录转移为 old目录;
4)下载并解压WordPress的新版本,解压到相应子目录,通过访问 blog/wp-admin/install.php 启动程序;
5)输入原数据库的访问账号,WordPress自动检查;
6)自动选择升级数据库,此前程序文件已经升级完毕;
7)使用原账号登录到升级后的WordPress后台,一切就绪;
8)还差原WordPress下的主题文件和资源文件,通过 cp -rf blog/wp-content/themes/xxx-theme 复制过来主题文件;
9) 通过 cp -rf blog/wp-content/uploads 复制过来资源文件,上传文件;
10) WordPress 提供了在线升级原主题的服务,顺利实施。
11)升级主题及相关文件之后,原先如果对主题模板文件进行了相应修改的话,需要重新在升级后的模板文件中进行再次有针对性的修改,比如特色链接,插图或者广告链接之类的插件。


-GWA2 吉娃兔 是”通用网络应用架构( General Web Application Architeture, https://ufqi.com/dev/gwa2/ )”,基于 -GWA2 可以轻便构建各种网络应用程序,
包括复杂的在线购物商城、在线医疗、在线教育、 旅游交易平台、社群或者社交网站和新闻资讯网站等,
也包括各种企事业单位网上门户,在线交互及服务作业系统等.
还可以包括为NativeApp做服务器端支持, 甚至是WebApp的全部.
-GWA2 是为数不多的支持跨开发语言的应用框架,目前支持 -Java, -PHP, -Perl, -Aspx and -Python .

-GWA2 is a “General Web Application Architecture” and based on -GWA2 developers can easily build a variety of network applications,
including complex online shopping malls, online medical services, online teaching, travel trading platforms, community or social networking sites and news information sites, etc.
Also the applications include various online portals of enterprises and institutions, online interaction and service operations systems.
Moreover it contains server-side support for Native App, or even all of the WebApp.
-GWA2 is one of the web frameworks which provide cross-language support for -Java, -PHP, -Perl, -Aspx and -Python at present.

-GWA2 is E.A.S.Y 
Easy Along, Swift Yield
轻松启动, 快速产出.


ufqinews logo
有福新闻 UfqiNews

这里呈现热点全局, 尺寸间一览所有令人关注的疑点焦点;
这里表达条分缕析, 视野内一睹各个脉络清晰的故事主线.
有福新闻UfqiNews 带来全新的资讯阅览体验, 不信息过载, 亦不信息茧房.

在寻求最大社会共识和满足千人千面之间谋取平衡,
在满足广泛涉猎与追求术业专攻之间谋取平衡.
媒介插上人工智能的翅膀将如虎添翼, 与资讯比翼双飞.
新闻爱好者的良心之选, 匠心之作.

UfqiNews presents the hot spots globally, with all interesting points at a glance.
Information is organized here and there is a clear storyline within every single detail.
UfqiNews brings a brand new reading experience, no information overload and no information Cocoons,

In seeking a balance of the maximum social consensus and meeting thousands of people for each interest,
In achieving a balance between satisfying a wide range of hunting and pursuing specialization in the industry.
That media is being born with wings of the artificial intelligence will be even more powerful and the information will fly swifter than ever.
Better choices of newsreaders and the art of work from them.

发表在 -GWA2, UfqiNews有福新闻, 社会生活, 编程技术, 计算机技术 | 标签为 , , , , | 一条评论

Hanjst汉吉斯特🙋与龙书编译原理

时间过得时而飞快,时而缓慢。我们人类已经与新冠肺炎疫情奋战了快两年了,好消息是随着疫苗的不断改进和持续的普及防护能力在提升,随着口服特效药面世和不断优化升级人类也不再不堪一击了。

就像不断改进和增强的疫苗和特效药一样,Hanjst汉吉斯特🙋( https://ufqi.com/dev/hanjst/ )也在持续的优化改进升级。回顾上一次更新是在 2021年3月份:Hanjst汉吉斯特🙋优化升级开发者模式( https://ufqi.com/blog/hanjst-updt-with-safari-tel/ ),差不多是年头一次主要升级,年尾一次主要升级。这次针对Hanjst汉吉斯特🙋改进,也是半年多来的累积改进,其中较大的亮点之一是改进针对注释语句的替换的操作。

在Hanjst汉吉斯特🙋的模板语言中,既有HTML的表述语言,也有原生的JavaScript程序代码,相应地会有HTML的注释语句,如
<!– 这是HTML注释内容 –> ,

也会有JavaScript的注释语句,如
//- 这是一个JavaScript注释行, 
/* 注释段落 JavaScript行1
     行2
     注释段落尾部
*/

其中在 JavaScript的单行注释时,这个注释既可以是一个单独的内容行,也可能是紧紧跟随在一段代码之后,如
const aString = “a-string-sample”; //- 声明一个JavaScript字符串变量

为兼容各种情况,程序逻辑事情变得异常的糟糕,经过很多次尝试之后,我们小结所遇到的困境和可能的出路时,目光一下子聚焦到被称之为”龙书(Dragon book)”的计算机程序编译的《编译原理》这本书。这本书的全民是 Compiler: Principles, Techniques and Tools (编译器: 原理,技术和工具), 在线内容收录在 有福经典UfqiLong: https://ufqi.com/news/clscpage.1075.html?tit=编译原理 Compilers: Principles, Techniques, and Tools 。

从本质上说,从模板语言到可执行的生成终端页面呈现HTML的其他程序语言,就是一次编译过程,这个过程与龙书编译原理所描述的过程一个模样。
compiler-fig-02.PNG

侠义的编译是将人类易读通用的程序语言代码“翻译”成为机器可以执行的机器语言代码。比如,将C语言程序代码“翻译”成为机器语言代码,将Java语言程序代码“翻译”成为机器语言代码。
Hanjst汉吉斯特🙋也做了类似的工作。整个翻译逻辑过程如下:

Hanjst模板语言代码 –> (翻译过程) –> JavaScript 语言程序代码 –> (翻译过程) –> 机器语言代码

这其中的“翻译过程”,就是编译器的工作过程,负责读入原语言代码,经过一系列的转换规则过滤与约束和改头换面的操作步骤,最终输出可供执行的目标语言代码程序。
如果所生成的目标代码程序是静态的文件程序代码,称之为静态语言,如果从abc.c 程序原代码到可执行的 abc.out.exe 程序文件。
对应的,如果所生成的目标程序是临时的内存态或者其他过程性临时文件,则称之为解释型语言,或者 动态语言,如直接执行 abc.js 或者 abc.pl 等,其编译后的临时文件只在运行时存在,执行完了或缓存下来或就直接被抛弃了。
整个链条中,Hanjst汉吉斯特只是做了第一个翻译过程,第二个翻译过程通常是有浏览器自带的 JavaScript编译和执行引擎来完成,比如 在 Google Chrome等浏览器中用到的 JavaScript V8 引擎 ( https://v8.dev/ )。

What is V8?

V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++. It is used in Chrome and in Node.js, among others. It implements ECMAScript and WebAssembly, and runs on Windows 7 or later, macOS 10.12+, and Linux systems that use x64, IA-32, ARM, or MIPS processors. V8 can run standalone, or can be embedded into any C++ application.

如果事情再往复杂方向继续发展,我们将不得不面临升级 Hanjst汉吉斯特的编译引擎到通用编译器的逻辑那样:
1)词法分析 Lexical  Analysis ,
2)语法分析 Syntax Anaylsis,
3) 语义分析 Semantic Analysis
编译原理 龙书 Compiler, Dragon Book-4:第一章:引论-2

目前,Hanjst汉吉斯特对模板语言的翻译尚未从最基础的词法分析,而是跳过将程序代码进行“标签化(Tokenized)” 的过程,上来就从语法分析入手,逐行对模板语句进行扫描,使用基于关键词的正则表达式对模板语句进行语义分析。经分析是当前行包括有 Hanjst汉吉斯特定义的语法关键词及相应表达式时,做如下转换:

Hanjst模板语言代码 –> (翻译过程) –> JavaScript 语言程序代码 

如果发现,当前行的内容没有相关关键词及相应的表达式时,直接跳过,将当前行视为一个普通常量字符串数据。

更上一层的语义分析的实现,则是基于语法语句分析的基础上,对包括条件控制语句、循环、退出等情况的处理,需要进行结束条件控制、关闭循环等语义层的操作时,根据一个结构数据表来进行一一对应补全。
这样的结构设计可以应付诸如模板语言这样的简单表达,同时也避免上来就进行Tokenized 的计算开销,可能是因地制宜的平衡之道吧。

然而,如果再复杂一些的情况,比如 Hanjst汉吉斯特这次修改的一个重点就是兼容行如下面的注释行,

const aString = “a-string-sample”; //- 声明一个JavaScript字符串变量

虽然几经周折,结合关键词和正则表达式成功在一些应用场景实现了预期目标,但也是逼仄式的采用了各种“奇技淫巧”——经验上看,约束条件越多,其普适性就会越差,甚至可以遇见,还有我们无法感知到的场景未被覆盖,“黑天鹅”也许就在远方。
这也许就是归纳法范式天然的缺陷。

也许,当下一只“黑天鹅”再出现时,就触及我们当前算法背后的极限,不得不使用《编译原理》中的名门正派的功夫来应对更加复杂的场景,以演绎、推理的形式彻底解决无穷尽的应用场景。

1)词法分析 Lexical  Analysis , –> 标签流Tokens Stream
2)语法分析 Syntax Analysis, –> 语法树 Syntax Tree
3) 语义分析 Semantic Analysis –> 语法树 Syntax Tree.

附加1:
来自JavaScript V8 Engine的页面JavaScipt提速优化建议
1) Improve download time, 提高JavaScript的脚步程序下载速度,一般不要超过 50KB , 再大了就拆小点, 对移动端尤其越要再小些。

2) Improve execution time, 提升执行速度,避免在主线程中放入 Long tasks. 这样才能提升页面渲染速度,尽早呈现出来。
Long tasks 是那些执行时间超过 50ms 的任务,Hanjst汉吉斯特完成翻译一个中等复杂程度的页面执行时间通常在 30ms左右。

Long task

Any uninterrupted period where the main UI thread is busy for 50 ms or longer. Common examples include:

  • Long running event handlers.
  • Expensive reflows and other re-renders.
  • Work the browser does between different turns of the event loop that exceeds 50 ms.

3) Avoid large inline scripts, 避免页面内直接书写太长JavaScript程序代码,如果内嵌代码长度超过 1KB,就考虑将其移入某个 .js 程序文件中。

4) JSON parse is faster than Object, 这似乎超出普通认知了…

const data = { foo: 42, bar: 1337 }; // 🐌
const data = JSON.parse('{"foo":42,"bar":1337}'); // 🚀

Hanjst 汉吉斯特 2021年近一年来的主要更新摘要:

* 09:08 2021-03-17, imprvs for debug in mobile browsers, +support for if conditionExpr
* 17:09 2021-04-26, + _enSafeExprAsCondition .
* 21:32 2021-05-19, bugfix for remedyMemoLine .
* 12:31 2021-05-21, bugfix for &amp; .
* 09:04 2021-06-07, add more _enSafeExpr .
* 21:51 2021-06-29, +comment: use Firefox to figure out exact error lineNumber and columnNumber in tpl2code in new Function
* 21:55 2021-11-11, imprvs for remedyMemoLine

更多参考 Hanjst汉吉斯特官网:https://ufqi.com/dev/hanjst/

 


Hanjst
Hanjst 汉吉斯特 Logo

🙋Hanjst汉吉斯特 是一种基于JavaScript的模板语言及模版解析引擎,她运行在客户端或服务器端。

🙋Hanjst汉吉斯特 能够表述逻辑控制,能够实现与服务器端模版语言相同的强大功能。

  • Hanjst当完全在客户端解析时,节省服务器端计算资源;

  • Hanjst模板语言独立,不与服务器端资源做任何绑定;

  • 纯粹的MVC,层间数据用JSON格式传递;

  • 常见模板语言功能全支持,附带复杂而强大的JavaScript编程能力;

  • 无学习成本,直接使用JavaScript书写模板语言;

  • ….

Hanjst is a JavaScript-based templating language and parsing engine that runs on both the client-side and/or server-side.

Hanjst can express logical controls and achieve the same functionalities as the server-side templating languages.

  • Hanjst’s Run-time in client-side, reduce computing render in server-side;

  • Hanjst is Language-independent, not-bound with back-end scripts or languages;

  • Totally-isolated between MVC, data transfer with JSON;

  • Full-support template tags with built-in logic and customized JavaScript functions;

  • No more tags languages to be learned, just JavaScript;

  • ….

Hanjst汉吉斯特🙋与龙书编译原理

 

发表在 -GTAjax, -Hanjst/-汉吉斯特, 编程技术, 计算机技术 | 标签为 , , , , | 留下评论

GWA2吉娃兔🐇Java中的文件上傳表單處理若干問題-2

去年(2020年)秋天写了上一篇:GWA2吉娃兔🐇Java中的文件上傳表單處理若干問題( https://ufqi.com/blog/gwa2-java-file-upload-issues/ ),随着我们持续不断的研发推进、精益求精,发现在 GWA2Java 中處理文件上傳的HTML表單被稱爲是“客貨混裝”的HTTP請求處理时,还有一些额外的问题需要再补充。兹详述于下,备忘备查。

(问题1-7请参考此前一篇Blog)

8. 数组型数据的接收与处理

在GWA2 Java中,为上传文件当HTML的表单类型被设置成 enctype 屬性為 multipart/form-data , request.getParameter 方法失效,同样地类似方法 request.getParameterValues 也失效了,如此以来,获取类似 多个 checkbox 这样的HTML form的输入,就成了问题。为此,我们需要再扩展一下之前使用 FormItems 来接管 request的相关属性的方法。

ServletFileUpload sfileupld = new ServletFileUpload((new DiskFileItemFactory()));
formItems = sfileupld.parseRequest(request); // can only be parsed once!
if (formItems != null && formItems.size() > 0){
String iname, ivalue; byte[] bytes; Object lastVal;
for (FileItem item : formItems){
// processes only fields that are common form fields
if (item.isFormField()){
bytes = item.getFieldName().getBytes(“ISO-8859-1”); // why 8859?
iname = new String(bytes, “UTF-8”);
bytes = item.getString().getBytes(“ISO-8859-1”);
ivalue = new String(bytes, “UTF-8”);
lastVal = request.getAttribute(iname);
if(lastVal != null){
// checkbox or multiple select
if(lastVal instanceof String[]){
String[] tmpArr = (String[])lastVal;
String[] tmpArr2 = new String[tmpArr.length+1];
for(int si=0; si<tmpArr.length; si++){
tmpArr2[si] = tmpArr[si];
}
tmpArr2[tmpArr.length] = ivalue;
request.setAttribute(iname, tmpArr2);
}
else{
String[] tmpArr = new String[2];
tmpArr[0] = String.valueOf(lastVal); tmpArr[1] = ivalue;
request.setAttribute(iname, tmpArr);
}
}
else{
request.setAttribute(iname, ivalue);
}
//debug(“ctrl/user: iname:”+iname+”, ivalue:”+ivalue+” lastVal:”+lastVal);
}
else{
debug(“ctrl/user: not form field: iname:”+item.getFieldName()+”, ivalue:”+item.getString().length());
}
}

使用 lastVal, 来检测是否同一个key的键值被赋予多个赋值,如果时,就启用一个数列来存储。相关代码在 github/wadelau/GWA2  ( https://github.com/wadelau/GWA2 )上可以获取。
该应用的场景是一个Form中,既有多选框,也有文件上传。

对应地,我们也在相关GWA2 的基础设施组件 comm/Wht 中增加了针对Form 的数列型数据的接收处理,方法名为
Wht.getArray(request, fieldName)
以此来接收和处理类似 multi select和check box类型的输入。

9. 多文件同时上传的优化处理

针对服务器端接收处理文件上传的相关组件,已经非常成熟,拿来用即可。如前所述,我们在GWA2 Java中处理文件的模块主要来自 Apache commons-fileupload-1.4.jar , 一并的也要引入 Apache commons-io-2.7.jar。

在一些细节上,还是有可深究的地方。默认情况下,我们通过 inc/FileSystem 中的一个 upload 方法来处理这些细节。针对 upload这个方法,预期的效果是给定一个上传的 form字段名称,然后返回一个保存后的文件路径,形式如:
formFieldA -> filePathInDisk

如果是多个文件呢?之前的方法是 upload 返回一个数组,包括多个地址,相当于:
[formFieldA, formFieldB] -> [filePathInDisk, filePathInDisk]

此时处理两个以上的文件上传时,它是能够完成预期目标的,可是,如果要更新其中一个文件时,就会出错。由于Form字段名称对应的磁盘文件路径,只有顺序,而没有最终预期的key/value 形式,当修改或更新其中一个文件时,总是返回形式为:
formFieldA -> filePathInDisk

这与只上传一个文件的情形是没法区分的。需要改正、优化。预期的是返回一个key/value形式的 formField/diskFilePath 。

// parses the request’s content to extract file data
@SuppressWarnings(“unchecked”)
List<FileItem> formItems = preFormItems;
if(formItems == null || formItems.size() ==0){
formItems = upload.parseRequest(request);
}
if (formItems != null && formItems.size() > 0){
// iterates over form’s fields
String fieldName, itemName, fileName, suffix, filePath;
File storeFile = null;
String showPath = UPLOAD_DIRECTORY + File.separator + relativePath;
if(!relativePath.endsWith(File.separator)){ showPath += File.separator; }
for (FileItem item : formItems){
// processes only fields that are not common form fields
if (!item.isFormField()){
fileName = “”;
fieldName = item.getFieldName(); itemName = item.getName();
if(itemName != null && !itemName.equals(“”)){
suffix = “”;
if(itemName.lastIndexOf(“.”)>0){
suffix = itemName.substring(itemName.lastIndexOf(“.”), itemName.length());
}
fileName = UUID.randomUUID().toString().replaceAll(“-“,””) + suffix;
filePath = uploadPath + File.separator + fileName;
debug(Log_Tag+” upload: filePath:”+filePath+” itemName:”+itemName);
// saves the file on disk
storeFile = new File(filePath);
item.write(storeFile);
fileName = showPath + fileName;
}
else{
fileName = “”;
debug(Log_Tag+”upload: skip empty itemName.”+itemName);
}
hmResult.put(fieldName, fileName);
}
else{
//debug(Log_Tag+” upload: form field item:”+item);
}
}
}
else{
debug(Log_Tag+” upload: formItems:”+formItems);
}

新增了 fieldName 字段,在返回时,将 Form fieldName 与 diskFilePath 以 key/value 形式严格对应起来,由返回 String 后者 String数组,改为返回 HashMap。如此以来,则可以解决同时上传多个文件,而不依赖顺序来对应所上传的文件,同时,当修改时,也可以避免所跳过的文件产生的顺序异常。完整功能的相关代码在 github/wadelau/GWA2  ( https://github.com/wadelau/GWA2 )上可以获取。

由于方法返回值的修改,不兼容此前版本,建议老版本的用户升级时,使用方法重载后者命名新方法的形式引入新功能。比如,在最近的项目 Boss选址 ( bossxuanzhi.com ) 中,我们就面临要使用命名新方法来兼容前期使用旧方法的 upload 和后期采用新方法的 uploadMultiple 。
如果是在新项目中,建议直接使用即可,没有向前兼容的需求。

 


-GWA2 吉娃兔 是”通用网络应用架构( General Web Application Architeture, https://ufqi.com/dev/gwa2/ )”,基于 -GWA2 可以轻便构建各种网络应用程序,
包括复杂的在线购物商城、在线医疗、在线教育、 旅游交易平台、社群或者社交网站和新闻资讯网站等,
也包括各种企事业单位网上门户,在线交互及服务作业系统等.
还可以包括为NativeApp做服务器端支持, 甚至是WebApp的全部.
-GWA2 是为数不多的支持跨开发语言的应用框架,目前支持 -Java, -PHP, -Perl, -Aspx and -Python .

-GWA2 is a “General Web Application Architecture” and based on -GWA2 developers can easily build a variety of network applications,
including complex online shopping malls, online medical services, online teaching, travel trading platforms, community or social networking sites and news information sites, etc.
Also the applications include various online portals of enterprises and institutions, online interaction and service operations systems.
Moreover it contains server-side support for Native App, or even all of the WebApp.
-GWA2 is one of the web frameworks which provide cross-language support for -Java, -PHP, -Perl, -Aspx and -Python at present.

-GWA2 is E.A.S.Y 
Easy Along, Swift Yield
轻松启动, 快速产出.

 


 

Boss选址( bossxuanzhi.com )是汇成产业服务(汇成产服)提供的在线服务,是服务政府投融资、招商、运营的平台型公司. 汇成产服公司定位:一、专业的政府招商代理服务机构,二、企业选址服务机构,三、企业咨询管理服务平台。

 

( https://ufqi.com/blog/gwa2-java-file-upload-issues-2nd/ )

 

发表在 -GWA2, 编程技术, 计算机技术 | 标签为 , , , , | 留下评论

理性经济人在边际上做选择

奥地利经济学派的大师米塞斯(Ludwig von Mises / 路德维希·冯·米塞斯)在《人的行为Human Action》论述到:“一个人决不会在「金」与「铁」之间选择,而只是在一定量的金与一定量的铁之间选择。”。( https://ufqi.com/news/ulongpage.1975.html )

我想用新近发生在我们自身的一个案例来试图描绘这种边际选择,而不是非此即彼的是非选择。
2021年10月,我们要为 ufqi.com 举行一些活动,制作若干 ufqi.com 的不干胶贴纸。为此我们先在 某宝网上翻阅,发现不能在网页与店家沟通,需要再安装一个聊天沟通客户端( wwcmd ),隧作罢。
然后尝试在 某东商城上翻阅,找到类似服务店家,直接在网页上进行沟通定制细节,然后按约定下单,隧成交。

针对制作 ufqi.com 不干胶胸贴纸这件需求或任务,所能采取的方法有很多(选择),既可以在楼下街边找一家快印店,也可以网上下单。选择在网上下单制作,还可能面临第二层选项,是在 某宝网上选,还是在某东商城上选?
即便是最终确定了一家某东商城,我们同样还会继续面临第三层选择,是选店家A还是选择店家B、C、D…?

从这个过程看,客户抵达的路径岔口太多异常艰辛,所有商家都不应该辜负客户的每一次光临。都应该殚精竭虑地完成客户的托付,才对得起客户在抵达商家之前的一次次的“临幸”。

我们为何不是那种,打死我也不去街边店下单? 或者打死也不去网上下单?
甚至在网上下单,当我们遇到某宝网上要求我们下载一个客户端才能与店家沟通的障碍时,为何不做出“打死以后也再来某宝网上购物了”的决定呢?

因为我们是理性经济人,我们在做决策时,是从经济的角度考虑,经济的潜台词中,主要内容就是在有限成本支出的前提条件下,获得最大化的产品和服务收益。

在上面这个案例中,表面上再多安装一个沟通客户端对用户来说是负担,是额外的成本开支,深层的是还有其他因素在里面。尽管商家要求用户安装沟通客户端有千万个理由,甚至其决策和设计过程也经过复杂无比的争论,但当遇到有理性思维,懂得做边际选择,有可能替代的产品或服务出现时,其被取代是必然的,剩下的只是时间问题。

我们拒绝再额外多安装一个沟通客户端还由于其行为或者方向与 ufqi.com 所倡导的 互联互通开放包容 的理念相悖,甚至是反向加速。某宝网不提供基于网页的沟通工具,某付宝不提供基于网页版的服务,某多多不提供网页版的服务,尽管是商家的自由,值得尊重,同样地,用户也有选择用或者不用的自由。

这也是为何我们必须旗帜鲜明地反垄断,垄断既是健康经济体的毒瘤,也是社会发展、文明进步的绊脚石。垄断的团体只有一个目的,攫取更多的利益以加固其垄断地位,直到永远,102年是远远不够的。

互联互通开放包容 的先决条件是任何技术或平台必需是多家共同协商制定和维护的。这是互联网络、万维网生存的前提,也是已经发展实践的正确道路。这期间总是有各种软件或(和)硬件公司试图在全局或者某些局部上下通吃左右把控,实施垄断以谋取暴利。

我们都不会忘记,没有万维网以前,所有的软件都是今天流行的App格式(Application 独立二进制程序包,需要独立地安装、运行以满足某种需求),分发途径就是物理磁盘复制拷贝人际传播交换。我们不想回退到史前,所以我们不遗余力地倡导继续保持互联互通、开放包容,基于万维网开发,基于浏览器或者另外一种基于开放技术平台(HTML50?)开发互联网络。
画地为牢的App是旧势力,是垄断、专制的保皇派的利器。
万维网才是打造人类巴别塔的基石。

我们,作为理性经济人,应该在力所能及的情况下,保卫奄奄一息的互联互通开放包容的精神火种,放大视野放长时光,我们的世界基于自由身份平等地位的分工协作才是征程大海。
我们坚信,基于浏览器或者另外某种新的开放技术平台(HTML50?),一定能实现自由地使用搜索引擎、新闻、娱乐、购物和通信等基本互联网服务。我们在一个大公司的垄断平台上忍了很久,可能还要继续再忍一阵子,但这并不代表我们绝望了,毕竟我们是理性的经济人。当我们看到一丝曙光时,代表着未来,引领了方向,就摇旗呐喊助威,表露我们的边际选择倾向。在一个混沌系统中,当我们的选择可以影响整体的发展时,那应该算是幸事。乐观其成。

经济学家张维迎教授在《经济学的十个基本原理》一文中这样解释边际选择:

经济学所要关注的问题,通常不是极端的非此即彼,而是“多一点”还是“少一点”的比较,也就是对“边际成本”和“边际收益”的权衡。
举例来说,我们需要考虑如何在购置食品和购置衣物上分配收入。一般来说,我们不会只买衣服不吃饭,也不会只吃饭不买衣服,而会考虑是多买些食物而少买些衣服,还是多买些衣服而少买些食品,这样的选择就是边际选择。
https://ufqi.com/news/ulongpage.243.html?tit=北大张维迎:你应该知道的10个经济学原理

这里的“衣物”与“食品”的对比,恰如开篇米塞斯举例中的“金”与“铁”一样。在上述制作 ufqi.com 的不干胶胸贴纸的任务描述中,也许我们投赞同和选择票的某东商城以后会基于自身利益考虑也要用户安装一个沟通客户端,也许我们反对和不选择的某宝网也许以后发现用户流失厉害还是改回在浏览器直接允许用户和店家沟通吧。这些都有可能,所幸我们都是理性经济人,当更加经济的方向出现时,我们总是能够用脚投票即可,在经济一边的边际上多买一些,在非经济一边少买一些。

可能的难题是,当信息不能自由流动时,人们因为“无知”而不懂得如何去做边际选择。这恰是所有画地为牢的App努力达成的愿景——“除我之外,再无别的神”。多年前,我们在人民网有个研究就发现,互联网络应用的发展趋势总是服务的排他性和内容的多样性不断集成和加强( 新闻阅读的聚合趋势–传媒–人民网 (people.com.cn),http://yjy.people.com.cn/n/2015/0701/c245081-27239100.html  )。

理性的经济人,我们天生要肩负这种责任,具备一定的抗拒能力,设法保持互联网络生态服务的多样性(每种应用服务两家以上,互联互通)和内容的排他性(文字影音每类内容公推两种默认格式必需支持,其他自选),维护互联互通开放包容的格局。

 

—-这是温习经济学著作的第八篇习作,之前的各篇附列如下。

  1.  写写年度收益率年均收益率和年化收益率-4 | -UFQI-Blog, https://ufqi.com/blog/income-rate-annuals-with-buffett/ 
  2. 写写年度收益率年均收益率和年化收益率-3, 
    https://ufqi.com/blog/income-rate-annuals-with-yale/
  3. 写写年度收益率年均收益率和年化收益率-2 , https://ufqi.com/blog/income-rate-annuals-with-cic/
  4. 写写年度收益率年均收益率和年化收益率, https://ufqi.com/blog/income-rate-annuals/
  5. 治大国若过小家——写写王朝兴衰更替背后的经济账, https://ufqi.com/blog/political-reform-country-vs-home/
  6.  写写🏦存款利率贷款利率和负利率, https://ufqi.com/blog/captial-rate-and-minus-rate/
  7. 写写1929年美国经济大萧条与2020年美国股市大跌, https://ufqi.com/blog/us-1929-economic-crisis-2020-stock-shock/
发表在 社会生活, 计算机技术 | 标签为 , , , | 留下评论

有福新闻UfqiNews: 没有富矿我们只提供了一张藏宝图

大约10天前(2021-07-02)我们撰写了 有福新闻UfqiNews 更新日志:《UfqiNews有福新闻更新:提质增效》( https://ufqi.com/blog/ufqinews-imprv-for-quality-experience/ )。这一次我们想写写 有福新闻UfqiNews 初心与使命,简要回顾一下几年来奔波儿灞、灞波儿奔式的改进过程中,核心追求和愿景是什么,以期不忘初心,牢记使命。

在有福新闻UfqiNews的简介页面上,写得多宏观、宏伟而宏大,又有点高远得不接地气。
“有福新闻UfqiNews 带来全新的资讯阅览体验, 不信息过载, 亦不信息茧房.
在寻求最大社会共识和满足千人千面之间保持适度,
在满足广泛涉猎与追求术业专攻之间谋取平衡.”

可能普通读者或者资讯消费者本不懂什么是信息过载,更诳论信息茧房。对于大众传播而言,所热火的流量依然逃不脱人性使然的“(娱乐明)星,(色)性,(血)腥“。诚如大众所言,很黄很暴力…, 这虽不入主流,确实流量的顶流。如同世界上其他一些害人的东西一样,明知有害,却屡禁不止,这三Xing的资讯可算作一类。

图文内容如此,音视频等多媒体内容也概莫能独善其身。一项由Mozilla软件基金会进行的针对Youtube视频站的名为“YouTube Regrets”调查发现:大约70%的观看次数是由推荐算法引导产生的,这其中大约71%被观看视频内容,被标记为”后悔看了”.  ( https://foundation.mozilla.org/en/campaigns/youtube-regrets/ )

世界还在逐渐变好,而不是日渐堕落,说明主流和台面上依然是积极向上、奋发有为的。这是毋庸置疑的,也是必须坚持的操守之一。有福新闻UfqiNews本初的立意是使用计算技术手段对资讯进行聚合整理,让紧要的、精华的内容能够流传得更广泛、更持久、更深入。

有福新闻UfqiNews没有富矿,我们只提供了一张藏宝图。

在这里按图索骥总能找到想要的资讯,虽然只言片语的不完整,却总是画龙点睛的关键之笔。有心挖掘,事情真相或许就在这些碎片式的故纸堆里,通过关键词的穿针引线式的指引,像藏宝图一样,带领用户最终捕捉到关键要点。

我们没有图书馆、资料库,但我们知道哪里有,我们不但知道哪里有,还知道哪些是“好的”,哪些是“不好的”。这个功能有些像传统的搜索引擎,但又不完全相同。如果与搜索引擎类比的话,有福新闻UfqiNews是一种资讯搜索引擎,但这种称呼也不恰当,有福新闻UfqiNews并不索引所有资讯站点,甚至每一个目标中的资讯站点也不是索引全部页面,而只是收录例如首页和栏目频道首页这样的关键部位、关键条目。

如此一路top-down下来,有福新闻UfqiNews将原本庞大、复杂无比的海量资讯减缩到可以计算的范围,取其有限的、有代表性的关键信息再行聚合,从而起到以点带面、窥一斑而知全貌作用。

“有福新闻UfqiNews 寻求最大可能的新闻资讯立场中立、内容客观、真实,数据准确。她是一种新的尝试和探索,第一次让公民社会的“第四权力”在阳光下按自由意志运行。”

借助于互联网络新技术,行业的细分使得单独的广告平台得以独立运行,在此前提下资讯平台可以独立于广告平台而运行,从而形成的以市场为基础的新的资讯传播秩序。资讯平台也许是首次可以不设置广告收入部门而运行。

以上这些理想也好、理论也罢,多只是纸上谈兵。具体实际若何,我们或能从下面一些实例中获得认知或验证。为了标记从有福新闻UfqiNews流出的流量,我们在流出的链接中加入了有福新闻UfqiNews的标记,按通用国际惯例,我们以参数 utm_source=ufqinews 作为标记,告诉下游页面该次访问来自有福新闻UfqiNews。

下面这些页面是我们使用 utm_source=ufqinews 进行检索得到的,这些带有ufqinews标记的页面被登记在内容里,说明作者当初获取内容时使用的场景是从 有福新闻UfqiNews前往目标页面的。

有福新闻UfqiNews 这张藏宝图已经帮助了很多人获得有用的信息,未来也一定会帮助更多的人获取有价值的信息和内容,逐渐掌握使用和驾驭资讯的能力,逐步提高辨识和分析水平。
正确地获取准确的资讯至关重要,因为——信息即权力。


ufqinews logo
有福新闻 UfqiNews

这里呈现热点全局, 尺寸间一览所有令人关注的疑点焦点;
这里表达条分缕析, 视野内一睹各个脉络清晰的故事主线.

有福新闻UfqiNews 带来全新的资讯阅览体验, 不信息过载, 亦不信息茧房.

在寻求最大社会共识和满足千人千面之间谋取平衡,
在满足广泛涉猎与追求术业专攻之间谋取平衡.

媒介插上人工智能的翅膀将如虎添翼, 与资讯比翼双飞.
新闻爱好者的良心之选, 匠心之作.

UfqiNews presents the hot spots globally, with all interesting points at a glance.
Information is organized here and there is a clear storyline within every single detail.

UfqiNews brings a brand new reading experience, no information overload and no information Cocoons,

In seeking a balance of the maximum social consensus and meeting thousands of people for each interest,
In achieving a balance between satisfying a wide range of hunting and pursuing specialization in the industry.

That media is being born with wings of the artificial intelligence will be even more powerful and the information will fly swifter than ever.

Better choices of newsreaders and the art of work from them.

发表在 UfqiNews有福新闻, 社会生活, 计算机技术 | 标签为 , , , , | 一条评论