[SAP ABAP开发技术总结]将文件存储到数据库表中,并可发送邮件
以后考虑将CONTENT_HEX字段定义为LRAW类型,这样直接存储二进制,数据不会膨胀一倍了(也可采用其他方式,请参考 数据共享与传递->DATABASE章节)
"Table: ZJZJPDF 表结构设计如下:
"Field name Key Data element Datatype Length
"MANDT X MANDT CLNT 3
"ROW_ORDER X NUMC 10
"CHR_COUNT INT4 10
"CONTENT_HEX LCHR 510 "注:内类型为 C,并非 X
"LCHR类型的字段前必须有一个INT4类型的字段,并且该字段不能是主键,该字段的值为后面
"CONTENT_HEX的字符个数,并且需要在插入表时一并进行插入,否则CONTENT_HEX字段的值不能Select出来
DATA: BEGIN OF xtab255 OCCURS 0,
x(255) TYPE x,"一个字节需使用两个十六进制字符来表示
END OF xtab255.
DATA: ctab255 TYPE STANDARD TABLE OF solisti1 WITH HEADER LINE.
DATA: BEGIN OF pdf_table OCCURS 0,
mandt TYPE zjzjpdf-mandt,
row_order TYPE zjzjpdf-row_order,
chr_count TYPE zjzjpdf-chr_count,
content_hex(510),"实为字符类型
END OF pdf_table.
********==========================================插入
CALL FUNCTION 'GUI_UPLOAD'
EXPORTING
filename = 'c:\1.txt'
filetype = 'BIN'
TABLES
data_tab = xtab255.
*CALL FUNCTION 'GUI_UPLOAD'
* EXPORTING
* filename = 'c:\1.JPG'
* filetype = 'BIN'
* TABLES
* "接收上传数据时,按收的内表行类型也可以是Char类型内表
* ",尽管是以BIN 模式上传的,因为该上传函数内部还是以
* "X类型视图来维护ctab255内表的
* data_tab = ctab255.
LOOP AT xtab255.
pdf_table-row_order = sy-tabix.
pdf_table-content_hex = xtab255-x.
"chr_count为内容content_hex字段值的长度,一定要设置,否则下次不能Select出来
pdf_table-chr_count = strlen( pdf_table-content_hex ).
APPEND pdf_table .
ENDLOOP.
DELETE FROM zjzjpdf."清空数据库表中的所有数据
MODIFY zjzjpdf FROM TABLE pdf_table[]."插入到数据库
********==========================================读取
CLEAR: pdf_table[].
SELECT * INTO TABLE pdf_table FROM zjzjpdf.
SORT pdf_table BY row_order.
CLEAR:xtab255[].
LOOP AT pdf_table.
"将数据库表中字面上的HEX字符串转换为真正的HEX二进制
xtab255-x = pdf_table-content_hex.
APPEND xtab255.
ENDLOOP.
"注意:虽然是原生态下载,但这里生成的文件在字节数上会多于源文本,并且字节数是255的整数倍,因为在上传的过程中使用的是xtab255类型的内表,当最后一行不满255个字节时,也会在后面补上直到255个字节,但补的都是00这样的字节,所以文件大小还是有区别的,但经过测试pdf与mp3在码流后面补00字节好像没有什么影响唯独是文件大小变大了。但如果是TXT文件,打开后发现多了很多空格(按理来说也是什么也没有,因为空格的ASCII码为32,但后面补的是00字节)
CALL FUNCTION 'GUI_DOWNLOAD'
EXPORTING
filename = 'c:\2.txt'
filetype = 'BIN'
TABLES
data_tab = xtab255.
*************************************************************************************
如果需要将PDF以邮件的形式发送出去时,上面的 xtab255 内表接口就已适合于使用cl_document_bcs类的add_attachment方法来发送邮件的i_att_content_hex内表参数接口;但如果使用SO_DOCUMENT_SEND_API1函数的方式来发送邮件时,并使用CONTENTS_BIN过时(可以使用新参参数CONTENTS_HEX内表接口时不需转换为下面的ctab255内表接口,也可以直接使用上面的xtab255内表接口)的内表参数接口传递PDF文件内容时,需要使用下面方式经过转换后的内表ctab255,即需要将上面的 xtab255 内表中每两行转换为 ctab255(行类型为 Char255)内表中的一行(因为一个X类型为一个字节,但在Unicode中一个C可存两个字节的内容,所以需二行转换一行操作)
*************************************************************************************
FIELD-SYMBOLS: <x> TYPE x.
DATA: c_tmp1(510) TYPE c.
DATA: c_tmp2(510) TYPE c.
DATA: c_total(1020) TYPE c.
DATA: tabix TYPE i,mod TYPE i.
CLEAR: ctab255[].
LOOP AT xtab255.
tabix = sy-tabix.
mod = tabix MOD 2.
IF mod = 1.
CLEAR: ctab255.
"原来是准备使用字段符号<c>(FIELD-SYMBOLS <c> type c) 指向xtab255-x使用 assign xtab255-x to <c>,但该语句使用时有限制:xtab255-x的类型的长度(所定义的字节数)一定要是4的倍数,否则编译出错,所以换成了现在这样,思路:将赋值双方中不是X类型的需要以X类型视图来操作,这样X类型与X类型赋值时不会发生数据的丢失或者是类型的转换,而不是将原本为X类型的一方以C或者其类型视图来看待,这样在非X类型间的赋值可能会导致数据丢失或发生类型转换
ASSIGN COMPONENT 'LINE' OF STRUCTURE ctab255 TO <x> CASTING.
c_tmp1 = xtab255-x.
ELSE.
c_tmp2 = xtab255-x.
CONCATENATE c_tmp1 c_tmp2 INTO c_total .
<x> = c_total. "因为4个字面上的十六进制字符才能表示一个字符C,所以从c_total以十六进制方式赋值给ctab255 时,缩短了3/4
APPEND ctab255.
CLEAR: c_tmp1.
ENDIF.
ENDLOOP.
"可能是奇数后,所以需要判断一下,将最后一行也要附加上去
IF c_tmp1 IS NOT INITIAL.
<x> = c_tmp1.
APPEND ctab255.
ENDIF.
FORM send_email.
DATA: BEGIN OF addr_email OCCURS 0,
smtp_addr(241),
END OF addr_email.
DATA: i_receivers LIKE somlreci1 OCCURS 0 WITH HEADER LINE,
i_message LIKE solisti1 OCCURS 0 WITH HEADER LINE.
DATA: l_subject TYPE so_obj_des,
l_filename TYPE string.
SELECT smtp_addr FROM zmm_invtr_email INTO TABLE addr_email.
CHECK addr_email[] IS NOT INITIAL.
LOOP AT addr_email.
CLEAR:i_receivers.
i_receivers-receiver = addr_email-smtp_addr.
i_receivers-rec_type = 'U'.
i_receivers-com_type = 'INT'.
* i_receivers-copy = 'X'."CC
* i_receivers-blind_copy = 'X'."BCC
i_receivers-express = 'X'."TO
APPEND i_receivers.
ENDLOOP.
l_subject = `hi,baby`."邮件标题
l_filename = 'attach.txt'."附件文件名
i_message-line = `邮件的内容`.
APPEND i_message.
DATA: t_atthead TYPE STANDARD TABLE OF string,
return TYPE bapiret1.
PERFORM send_email_with_att_xls TABLES i_receivers
i_message
t_atthead
<stat_tab>
USING l_subject
sy-uname
l_filename
return.
* MESSAGE return-message TYPE 'S' DISPLAY LIKE return-type.
ENDFORM. "export_xls
*&---------------------------------------------------------------------*
*& Form send_email_with_att_xls
*&---------------------------------------------------------------------*
* 带XLS附件的邮件发送
*----------------------------------------------------------------------*
* -->T_RECEIVERS 邮件接收者
* -->T_MESSAGE 邮件内容
* -->T_ATTHEAD XLS附件中标题(XLS首行内容)
* -->T_ATTTAB 附件内容
* -->IM_SUBJECT 邮件标题
* -->IM_UNAME 邮件发送者
* -->IM_FILENAME 附件名称
* -->RETURN 返回信息
*----------------------------------------------------------------------*
FORM send_email_with_att_xls TABLES t_receivers STRUCTURE somlreci1
t_message STRUCTURE solisti1
t_atthead
t_atttab
USING im_subject TYPE so_obj_des
im_uname TYPE uname
im_filename TYPE string
return TYPE bapiret1.
CONSTANTS: c_tab TYPE c VALUE cl_abap_char_utilities=>horizontal_tab,
"回车换行,实质上对应 Xstring: 000D000A
c_crlf(2) TYPE c VALUE cl_abap_char_utilities=>cr_lf.
DATA: xstr TYPE xstring,
it_binary_attach TYPE solix_tab.
************************将内表所有的内容存储到一个字符串中
DATA: lc_descr_ref TYPE REF TO cl_abap_structdescr,
lv_value TYPE char128,
lv_temp TYPE string,
lv_mid TYPE string.
FIELD-SYMBOLS: <fs_table> TYPE ANY.
FIELD-SYMBOLS: <intable_wa> TYPE abap_compdescr.
CLEAR lv_temp.
"将头内表拼接成一个字符串
IF t_atthead[] IS NOT INITIAL.
LOOP AT t_atthead.
IF sy-tabix = 1.
lv_temp = t_atthead.
CONTINUE.
ENDIF.
CONCATENATE lv_temp t_atthead
INTO lv_temp SEPARATED BY c_tab.
ENDLOOP.
CONCATENATE lv_temp c_crlf INTO lv_mid.
ENDIF.
"将主体内表拼接成一个字符串
LOOP AT t_atttab.
lc_descr_ref ?= cl_abap_typedescr=>describe_by_data( t_atttab ).
LOOP AT lc_descr_ref->components ASSIGNING <intable_wa>.
ASSIGN COMPONENT sy-tabix OF STRUCTURE t_atttab TO <fs_table>.
"如果是数字类型时
IF <intable_wa>-type_kind = 'P' OR <intable_wa>-type_kind = 'F'
OR <intable_wa>-type_kind = 'I' OR <intable_wa>-type_kind = 'b'
OR <intable_wa>-type_kind = 's'.
IF <fs_table> = 0.
CLEAR lv_value.
ELSE.
WRITE <fs_table> TO lv_value.
CONDENSE lv_value NO-GAPS.
IF <fs_table> < 0."将负号提前
CALL FUNCTION 'CLOI_PUT_SIGN_IN_FRONT'
CHANGING
value = lv_value.
CONDENSE lv_value NO-GAPS.
ENDIF.
ENDIF.
ELSE.
lv_value = <fs_table>.
CONDENSE lv_value.
ENDIF.
IF sy-tabix = 1.
lv_temp = lv_value.
CONTINUE.
ENDIF.
CONCATENATE lv_temp lv_value
INTO lv_temp SEPARATED BY c_tab.
ENDLOOP.
CONCATENATE lv_mid lv_temp c_crlf INTO lv_mid.
ENDLOOP.
************************将字符进行编码转换,转换为二进制字节码流
DATA: l_codepage(4) TYPE n .
DATA: l_encoding(20).
"将外部字符集名转换为内部编码
CALL FUNCTION 'SCP_CODEPAGE_BY_EXTERNAL_NAME'
EXPORTING
external_name = 'UTF-8'
IMPORTING
sap_codepage = l_codepage.
l_encoding = l_codepage.
DATA: convout TYPE REF TO cl_abap_conv_out_ce.
"创建编码对象
convout = cl_abap_conv_out_ce=>create( encoding = l_encoding ).
convout->write( data = lv_mid )."编码
xstr = convout->get_buffer( )."获取码流
************************接口转换:将码流按使用的参数接口要求进行重组转换
"将 xstring 的字符串存储到行结构为 X 类型的内表中,以适合于邮件发
"送接口。该函数并不是将 xstring 字符转换为 二进制(因为xstring本身就
"是原生态,从函数名上看容易误导)。
CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
EXPORTING
buffer = xstr
TABLES
binary_tab = it_binary_attach.
*另外,也可以将行结构为 X 类型的内表中所有HEX内容拼接成一个Xstring并存储在l_xstr变量中,也不要被函*数名给迷糊了:并不是将二进制什么的转换为Xstring类型了,而只不过是将将内表结构转换为单一的字符串了
*data: l_xstr type xstring.
* clear l_xstr.
* call function 'SCMS_BINARY_TO_XSTRING'
* exporting
* input_length = l_len
* importing
* buffer = l_xstr
* tables
* binary_tab = it_binary_attach.
*-----------------------------------------------------------------------
* Send Email
*-----------------------------------------------------------------------
DATA: lv_title TYPE so_obj_des, " Email Title
lv_email TYPE adsmtp-smtp_addr, " Receiver
lv_attitle TYPE char50. " Attachment Title
DATA: send_request TYPE REF TO cl_bcs,
document TYPE REF TO cl_document_bcs,
conlengths TYPE so_obj_len,
html TYPE TABLE OF w3html,
recipient TYPE REF TO if_recipient_bcs,
sent_to_all TYPE os_boolean,
bcs_exception TYPE REF TO cx_bcs,
bcs_message TYPE string.
DATA: sender TYPE REF TO cl_sapuser_bcs.
TRY.
* 创建发送请求
CLEAR send_request.
send_request = cl_bcs=>create_persistent( ).
CLEAR: lv_title.
lv_title = im_subject.
* 创建并设置邮件文档
CLEAR html.
REFRESH html.
html = t_message[].
CLEAR document .
document = cl_document_bcs=>create_document(
i_type = 'RAW'
i_text = html
i_length = conlengths
i_subject = lv_title ).
CALL METHOD send_request->set_document( document ).
****************这里也可以从数据库表中读取码流
* DATA: BEGIN OF xtab255 OCCURS 0,
* x(255) TYPE x,
* END OF xtab255.
* FIELD-SYMBOLS: <cc> TYPE c.
*
* DATA: ctab255 TYPE STANDARD TABLE OF solisti1 WITH HEADER LINE.
*
* DATA: BEGIN OF pdf_table OCCURS 0,
* mandt TYPE zjzjpdf-mandt,
* row_order TYPE zjzjpdf-row_order,
* chr_count TYPE zjzjpdf-chr_count,
* content_hex(510),
* END OF pdf_table.
*
* CLEAR: pdf_table[].
* SELECT * INTO TABLE pdf_table FROM zjzjpdf.
* SORT pdf_table BY row_order.
*
* CLEAR: xtab255[].
* LOOP AT pdf_table.
* "将字面上的HEX转换为真正的HEX
* xtab255-x = pdf_table-content_hex.
* APPEND xtab255.
* ENDLOOP.
****************************
* 添加附件:如果有多个,则需要调用该方法多次
lv_attitle = im_filename.
CALL METHOD document->add_attachment
EXPORTING
* i_attachment_type = 'BIN'"在邮件中点击附件会弹出文件保存对话框。其他类型请参考SAPFSSOA程序include文件RSSOCONS
* i_attachment_type = 'PDF'
* i_attachment_type = 'TXT'
i_attachment_type = 'XLS'"文件类型,在邮件中就可以打开文件,不会弹出保存对话框
i_attachment_subject = lv_attitle
i_att_content_hex = it_binary_attach.
* i_att_content_hex = xtab255[].
* 设置发送者
sender = cl_sapuser_bcs=>create( im_uname ).
CALL METHOD send_request->set_sender
EXPORTING
i_sender = sender.
* 设置接收者
CLEAR recipient.
LOOP AT t_receivers.
CLEAR lv_email.
lv_email = t_receivers-receiver.
recipient =
cl_cam_address_bcs=>create_internet_address( lv_email ).
CALL METHOD send_request->add_recipient
EXPORTING
i_recipient = recipient
i_express = t_receivers-express
i_copy = t_receivers-copy
i_blind_copy = t_receivers-blind_copy.
ENDLOOP.
* E-mail
CALL METHOD send_request->set_status_attributes
EXPORTING
i_requested_status = 'E'
i_status_mail = 'E'.
CALL METHOD send_request->set_send_immediately( 'X' ).
* 发送邮件
CALL METHOD send_request->send(
EXPORTING
i_with_error_screen = 'X'
RECEIVING
result = sent_to_all ).
COMMIT WORK AND WAIT.
IF sent_to_all = 'X'.
return-message = 'E-mail send successfully'.
return-type = 'S'.
ELSE.
return-message = 'E-mail send unsuccessfully'.
return-type = 'E'.
ENDIF.
CATCH cx_bcs INTO bcs_exception.
bcs_message = bcs_exception->get_text( ).
return-message = bcs_message.
return-type = 'E'.
EXIT.
ENDTRY.
ENDFORM.