通过 ABAP 将 XLSX 文件作为邮件附件发送

问题描述

我必须创建一封电子邮件并附上一个 XLSX 文件。我查看了 BCS_EXAMPLE_7 程序。

我使用以下方法转换了内容

  TRY.
      cl_bcs_convert=>string_to_solix(
      EXPORTING
        iv_string   = lv_content
        iv_codepage = '4103'  
        iv_add_bom  = 'X'
      IMPORTING
        et_solix  = pt_binary_content
        ev_size   = pv_size ).
    CATCH cx_bcs.
      ls_return-type    = text-023.
      ls_return-message = text-024.
      APPEND ls_return TO pt_return.
  ENDTRY.

  
      CONCATENATE lv_save_file_name '_' sy-datum '.xlsx' INTO lv_save_file_name.
      lv_attachment_subject  = lv_save_file_name.
      CONCATENATE '&SO_FILENAME=' lv_attachment_subject INTO ls_attachment_header.
      APPEND ls_attachment_header TO lt_attachment_header.

  
      lo_document->add_attachment( i_attachment_type    = 'XLS' 
                                   i_attachment_subject = lv_attachment_subject
                                   i_attachment_size    = pv_size
                                   i_att_content_hex    = pt_binary_content
                                   i_attachment_header  = lt_attachment_header ).

电子邮件已正确发送,但当我打开附件时看到错误

无法打开文件,因为文件扩展名不正确

你能帮我吗?谢谢

解决方法

这是 Excel 的正常行为,与 ABAP 无关,当文件名具有扩展名 .xlsx 但不包含格式对应于 XLSX 的数据时。 Excel 对其他扩展程序执行相同类型的检查。如果您需要有关这些检查的更多信息,请搜索网络。

当我看到您的程序根据转换为 UTF-16LE 代码页(SAP 代码页 4103)的文本创建附件时,我猜您以 CSV、制表符分隔值甚至旧 Excel 格式创建了 Excel 数据XMLSS/XML 2003 格式。

在这种情况下,扩展名 .xlsx 无效,为避免该消息,使用适当的扩展名,分别为 .csv.txt 或 {{ 1}}。

如果您出于某种原因确实需要扩展名 .xml,那么您必须以 XLSX 格式创建数据。您可以使用免费的 API abap2xlsx。如果您需要有关如何使用 abap2xlsx 的进一步帮助,请提出一个新问题(与电子邮件无关)。

注意:也许您被告知使用扩展名 .xlsx,尽管实际上没有必要使用它(每种格式都有自己的功能,但所有格式都可以实现简单的无格式值),在这种情况下您可以建议使用简单的格式,例如 CSV 或制表符分隔值。

注意:您可能也有相反的情况,即 Excel sniffs 文件包含格式对应于 XLSX 的数据,但文件名没有扩展名 .xlsx,对于相同的所有其他格式,但我不能说每种情况下 Excel 的确切反应是什么。

,

看来 lv_content 中的任何内容实际上都不是有效的 excel 文件。您不能只是获取任意数据,为其添加扩展名 .xlsx 并期望 MS Excel 知道如何处理它。

不幸的是,创建有效的 MS Office 文件绝非易事。这种格式理论上是开放的并基于 XML(实际上是一个包含多个 XML 文件的 zip 存档),但实际上 the specification 的长度超过 5000(!) 页。

幸运的是,有一个图书馆。 abap2xlsx 是一个开源(Apache 许可)库,它提供了一个简单的 API 来在 ABAP 中创建(和读取)有效的 XLSX 文件。

,

您也可以尝试使用文本编辑器(例如 NotePad++)打开文件,这可能会暗示实际内容。 但我猜生成二进制表时出了点问题。也许您使用了错误的文件大小或代码页。

,

可能的问题:

第一个问题:正如 Sandra 所说的那样,您的 lv_content 变量的内容可能无效,这与正确的 XLSX 结构不符。

第二个问题:您已经解决了,从您的编码中可以看出,BCS 类确实 not 支持 4 个字符的扩展名。

以下是如何通过邮件构建和发送正确的 XLSX 文件的示例:

SELECT * UP TO 100 ROWS
  FROM spfli
  INTO TABLE @DATA(lt_spfli).

cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lr_table)
                        CHANGING t_table = lt_spfli ).

DATA: lr_xldimension TYPE REF TO if_ixml_node,lr_xlworksheet TYPE REF TO if_ixml_element.

DATA(lv_xlsx) = lr_table->to_xml( if_salv_bs_xml=>c_type_xlsx ).
DATA(lr_zip) = NEW cl_abap_zip( ).
lr_zip->load( lv_xlsx ).
lr_zip->get( EXPORTING name = 'xl/worksheets/sheet1.xml' IMPORTING content = DATA(lv_file) ).

DATA(lr_file) = NEW cl_xml_document( ).
lr_file->parse_xstring( lv_file ).
* Row elements are under SheetData
DATA(lr_xlnode) = lr_file->find_node( 'sheetData' ).
DATA(lr_xlrows) = lr_xlnode->get_children( ).
* Create new element in the XML file
lr_xlworksheet ?= lr_file->find_node( 'worksheet' ).
DATA(lr_xlsheetpr)   = cl_ixml=>create( )->create_document( )->create_element( name = 'sheetPr' ).
DATA(lr_xloutlinepr) = cl_ixml=>create( )->create_document( )->create_element( name = 'outlinePr' ).
lr_xlsheetpr->if_ixml_node~append_child( lr_xloutlinepr ).
lr_xloutlinepr->set_attribute( name = 'summaryBelow' value = 'false' ).
lr_xldimension ?= lr_file->find_node( 'dimension' ).
lr_xlworksheet->if_ixml_node~insert_child( new_child = lr_xlsheetpr ref_child = lr_xldimension ).
* Create xstring and move it to XLSX
lr_file->render_2_xstring( IMPORTING stream = lv_file ).
lr_zip->delete( EXPORTING name = 'xl/worksheets/sheet1.xml' ).
lr_zip->add( EXPORTING name = 'xl/worksheets/sheet1.xml' content = lv_file ).
lv_xlsx = lr_zip->save( ).

DATA lv_size     TYPE i.
DATA lt_bintab TYPE solix_tab.

* Convert to binary
CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
  EXPORTING
    buffer        = lv_xlsx
  IMPORTING
    output_length = lv_size
  TABLES
    binary_tab    = lt_bintab.

DATA main_text      TYPE bcsy_text.
* create persistent send request
DATA(send_request) = cl_bcs=>create_persistent( ).
* create document object from internal table with text
APPEND 'Valid Excel file' TO main_text.
DATA(document) = cl_document_bcs=>create_document( i_type = 'RAW' i_text = main_text i_subject = 'Test Created for stella' ).

DATA lt_att_head TYPE soli_tab.
APPEND '<(>&< )>SO_FILENAME=MySheet.xlsx' TO lt_att_head.
* add the spread sheet as attachment to document object
document->add_attachment(
  i_attachment_type    = 'xls'
  i_attachment_subject = 'MySheet'
  i_attachment_size    = CONV so_obj_len( lv_size )
  i_attachment_header  = lt_att_head
  i_att_content_hex    = lt_bintab ).

send_request->set_document( document ).
DATA(recipient) = cl_cam_address_bcs=>create_internet_address( 'some_recipient@mail.com' ).
send_request->add_recipient( recipient ).
DATA(sent_to_all) = send_request->send( i_with_error_screen = 'X' ).

COMMIT WORK.