不使用任何框架,手写纯 JavaScript 实现上传本地文件到 ABAP 服务器

这是 Jerry 2021 年的第 69 篇文章,也是汪子熙公众号总共第 346 篇原创文章。

采用 SAP UI5 sap.ui.unified.FileUploader 控件,结合 Gateway 框架,实现本地文件上传到 ABAP 服务器,不是一件困难的事情。

不使用任何框架,手写纯 JavaScript 实现上传本地文件到 ABAP 服务器_第1张图片

但由于项目实施的客观条件限制,如果不使用 SAP UI5 和 SAP ABAP Gateway 这两个框架,又该如何实现呢?

这是最近一个朋友咨询我的问题。实际上我早在 2014 年就做过类似的事情。本文介绍不使用 SAP UI5 和 SAP ABAP Gateway 框架,纯手工进行前后端编程,来实现文件上传的需求步骤。

本文提供的源代码,前端 JavaScript 代码和 后端 ABAP 服务器的文件接收代码,由于没有使用任何应用层面的框架,因此能够在 SAP 任何基于 ABAP 技术栈的 On-Premises 产品里运行。

我们从前后端两层的实现来分别了解这个需求的实现细节。

前端实现

前端代码比较简单,只有 30 行代码。新建一个 HTML 文件,把这 30 行代码粘贴进去。

其设计思路概述成以下 7 点。需要完整源代码的朋友,请从这个链接获得。

(1) 定义一个原生的 form 元素,使用 enctype 字段指定该表单发送到服务器的编码格式为 multipart/form-data.

(2) 该表单的数据使用 HTTP POST 方法发送到服务器。

(3) 表单里包含了三个 input 控件,类型分别为 email, text 和 file. 本文介绍的本地文件上传功能,就是借助第 8 行类型为 file 的 input 控件来完成的。点击第 11 行用 a 标签实现的超链接后,绑定到 a 标签的 sendForm 函数触发,进行数据发送工作。

不使用任何框架,手写纯 JavaScript 实现上传本地文件到 ABAP 服务器_第2张图片

(4) 将 form 表单里全部数据通过 DOM API document.forms.namedItem 解析出来,放入变量 oData.

(5) 第 17 行语句演示了通过代码的方式,往待发送往服务器的表单数据里,再添加新内容的方法。

(6) 该行维护了表单数据发送到 ABAP 服务器的具体地址,其 SICF 路径为 /sap/crm/file_upload.

文件上传到 ABAP 服务器后,我们如何验证上传是否成功,内容是否正确呢?出于验证目的,我硬编码了一个销售订单 ID 55824.文件上传成功后,我在 SAP CRM 系统里,将上传的文件创建为该销售订单的一个附件。

(7) 调用原生 API XMLHttpRequest 的 send 方法,把表单数据传送到 ABAP 服务器。

打开这个 HTML 文件,如下图所示,点击超链接进行文件上传。

不使用任何框架,手写纯 JavaScript 实现上传本地文件到 ABAP 服务器_第3张图片

待上传的文本文件内容如下:

不使用任何框架,手写纯 JavaScript 实现上传本地文件到 ABAP 服务器_第4张图片

ABAP 后端实现

因为我们不借助任何后端框架,这意味着我们必须基于最底层的 HTTP 协议,自行解析出客户端发送过来的 multipart/form-data 格式的数据并进行处理。

首先在事物码 SICF 里,根据前端代码里的路径 sap/crm/file_upload, 新建一个同样路径的处理节点。

不使用任何框架,手写纯 JavaScript 实现上传本地文件到 ABAP 服务器_第5张图片

关于 SICF 的更多介绍,参考 Jerry 的文章:从ABAP Netweaver的SICF到SAP Kyma的Lambda Function.

给这个节点创建一个处理类 ZCL_FILE_UPLOAD, 本文所有的后端处理逻辑,就编写在这个类的 HANDLE_REQUEST 内。

不使用任何框架,手写纯 JavaScript 实现上传本地文件到 ABAP 服务器_第6张图片

完整的后端代码同样能从这个链接获得,本文不全部贴出,只介绍要点。

通过前端 form 表单三个 input 控件维护的输入值,加上前端代码中自定义的表单数据,被浏览器随机生成的 FormBoundary 所分隔。提交表单的完整数据,能够在 Chrome 开发者工具 Network 标签页里观察到。

不使用任何框架,手写纯 JavaScript 实现上传本地文件到 ABAP 服务器_第7张图片

ZCL_FILE_UPLOAD 类的实现逻辑为,首先解析出浏览器发送过来的 FormBoundary 标识符,根据这个 Boundary,把接收到的表单数据拆分成块,然后只处理我们感兴趣的包含本地文件上传的那一块。

不使用任何框架,手写纯 JavaScript 实现上传本地文件到 ABAP 服务器_第8张图片

我们刚刚展示了在 Chrome 开发者工具里查看发往 ABAP 服务器的表单数据明细,这些数据在 ABAP 服务器端接收之后,在调试器里显示如下:

不使用任何框架,手写纯 JavaScript 实现上传本地文件到 ABAP 服务器_第9张图片

这些井号代表什么含义?使用上图高亮的下拉菜单,把 Fast Display 切换成 HTML Browser,就一目了然了。原来这些换行符和回车换行符等控制字符,在 ABAP 调试器里的 Fast Display 视图里,统一显示为 “#”号。

不使用任何框架,手写纯 JavaScript 实现上传本地文件到 ABAP 服务器_第10张图片

我真正感兴趣的上传文件的实际内容,就存储在下图所示这个 form-data 块里。

不使用任何框架,手写纯 JavaScript 实现上传本地文件到 ABAP 服务器_第11张图片

因此,我的思路就是,根据回车换行符定位到上图 ABAP 变量 LV_DATA 包含的 form-data 块,如果该块包含了 content-type:text/plain,就说明此块包含的是上传文件的实际内容,对其解析即可得到上传文件的实际内容。

这里不少新手朋友们常犯的错误是,因为在 ABAP 调试器里观察到的回车换行符显示为“##”,因此在代码里,这些朋友也直接用单井号或者双井号进行字符串匹配,这当然无法工作。

在 ABAP 里要进行和换行符以及回车换行符相关操作,需要使用 ABAP 工具类 CL_ABAP_CHAR_UTILITIES 定义的常量:CR_LF 和 NEWLINE:

不使用任何框架,手写纯 JavaScript 实现上传本地文件到 ABAP 服务器_第12张图片

正如我本文实现代码里的使用方式:

不使用任何框架,手写纯 JavaScript 实现上传本地文件到 ABAP 服务器_第13张图片

解析出上传的文本文件内容后,调用 SAP CRM 附件创建 API,将该文件内容作为一个附件,添加到系统 ID 为 55824 的销售订单中去。

本例为了简化起见,只支持类型为文本(text/plain)的本地文件上传成销售订单(其 BOR 类型为 BUS20000115)的附件,故文件类型和 BOR 类型都进行了硬编码。

不使用任何框架,手写纯 JavaScript 实现上传本地文件到 ABAP 服务器_第14张图片

前后端开发完成之后,进行测试,从本地上传一个名为 upload.txt 的文本文件,ABAP 服务器接收到之后,将其存储成为销售订单 55824 的一个附件。

不使用任何框架,手写纯 JavaScript 实现上传本地文件到 ABAP 服务器_第15张图片

点击附件超链接,打开该文件内容,发现和本地文件完全一致,测试通过:
不使用任何框架,手写纯 JavaScript 实现上传本地文件到 ABAP 服务器_第16张图片

当然,本文描述的实际是一个重新造轮子的场景。大家在实际项目开发中,如果没有特殊原因,还是尽量采用 SAP 提供的现成框架和工具,来完成诸如文件上传这种比较基础和底层的工作,从而把精力放到业务逻辑的编写中去。

感谢阅读。

Jerry 的 ABAP 专题

更多Jerry的原创文章,尽在:"汪子熙":

你可能感兴趣的:(不使用任何框架,手写纯 JavaScript 实现上传本地文件到 ABAP 服务器)