【仅供内部供应商使用,不提供对外解答和培训】

Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of Contents

一、特殊名词介绍

二、背景、场景介绍

帆软产品中一个非常方便的功能就是导出。使用频率也非常的高。对于常见的一些文件类型的导出,目前产品本身都已支持。但是在一些特殊的行业或者场景中,用户往往有自己的文件规范和导出规范,在帆软产品中专门开放了用于解决这类导出的接口。

可以用于导出的接口比较多,而ExportOperateProvider则主要用于新文件类型的导出使用。使用也跟一般的产品导出一致,通过URL中指定format参数调用。

三、接口介绍

Code Block
languagejava
themeEclipse
firstline1
titleExportOperateProvider.java
linenumberstrue
package com.fr.report.fun;

import com.fr.stable.fun.mark.Mutable;
import com.fr.web.core.reserve.Operate;

/**
 * Created by richie on 16/1/19.
 * 自定义导出方式接口,推荐使用format=xxx的方式(xxx表示唯一的导出方式)
 */
public interface ExportOperateProvider extends Mutable {

    int CURRENT_LEVEL = 1;

    String MARK_STRING = "ExportOperateProvider";

    /**
     * 导出准备操作
     *
     * @return 操作对象
     */
    Operate operate();

    /**
     * 导出类型
     *
     * @return 类型
     */
    String markType();
}


Code Block
languagejava
themeEclipse
firstline1
titleBaseOperate.java
linenumberstrue
package com.fanruan.api.report.export;

import com.fr.io.collection.ExportCollection;
import com.fr.stable.web.SessionProvider;
import com.fr.web.core.reserve.Operate;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author richie
 * @version 10.0
 * Created by richie on 2019/9/26
 * 导出接口的抽象类
 */
public abstract class BaseOperate implements Operate {

    @Override
    public void setContent(HttpServletRequest req, HttpServletResponse res, String fileName, boolean isEmbed) {

    }

    @Override
    public void setContent(HttpServletRequest req, HttpServletResponse res, SessionProvider sessionProvider, String fileName, boolean isEmbed) {
        this.setContent(req, res, fileName, isEmbed);
    }


    /**
     * 导出的实体操作
     *
     * @param req             http请求
     * @param res             http响应
     * @param sessionProvider 模板访问会话信息
     * @param fileName        文件名
     * @return 导出实体操作对象
     */
    @Override
    public abstract ExportCollection newExportCollection(HttpServletRequest req, HttpServletResponse res, SessionProvider sessionProvider, String fileName);
}


Code Block
languagejava
themeEclipse
firstline1
titleOperate.java
linenumberstrue
collapsetrue
package com.fr.web.core.reserve;

import com.fr.io.collection.ExportCollection;
import com.fr.stable.web.SessionProvider;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Created by richie on 16/1/19.
 */
public interface Operate {

    void setContent(HttpServletRequest req, HttpServletResponse res, String fileName, boolean isEmbed);

    void setContent(HttpServletRequest req, HttpServletResponse res, SessionProvider sessionIDInfor, String fileName, boolean isEmbed);

    ExportCollection newExportCollection(HttpServletRequest req, HttpServletResponse res, SessionProvider sessionIDInfor, String fileName);
}


Code Block
languagejava
themeEclipse
firstline1
titleExportCollection.java
linenumberstrue
collapsetrue
package com.fr.io.collection;

import com.fr.common.annotations.Open;
import com.fr.general.DeclareRecordType;
import com.fr.general.RecordType;
import com.fr.io.exporter.AppExporter;
import com.fr.log.FineLoggerFactory;
import com.fr.log.LogUtils;
import com.fr.web.core.ErrorHandlerHelper;
import com.fr.web.core.ReportRepositoryDeal;
import com.fr.web.core.ReportSessionIDInfor;
import com.fr.web.core.utils.ExportUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;

/**
 * Created by richie on 15/11/30.
 * 导出操作器的一个封装
 */
@Open
public class ExportCollection {
    private AppExporter exporter;
    private RecordType recordType;

    public static ExportCollection create() {
        return new ExportCollection();
    }

    private ExportCollection() {
    }

    public AppExporter getExporter() {
        return exporter;
    }

    public void setExporter(AppExporter exporter) {
        this.exporter = exporter;
    }

    public RecordType getRecordType() {
        return recordType;
    }

    public void setRecordType(RecordType recordType) {
        this.recordType = recordType;
    }
    
    /**
     * 兼容方法
     * 可用同名方法。
     */
    @Deprecated
    public void setRecordType(DeclareRecordType recordType) {
        this.recordType = recordType;
    }

    public void doExport(HttpServletRequest req, HttpServletResponse res, ReportSessionIDInfor sessionIDInfor, String format)
            throws Exception {

        if (getRecordType() == null) {
            ErrorHandlerHelper.getErrorHandler().error(req, res,
                    "Cannot recognize the specifed export format:" + format +
                            ",\nThe correct format can be PDF,Excel,Word,SVG,CSV,Text or Image.");
            return;
        }

        OutputStream outputStream = res.getOutputStream();
        try {
            // 导出并且记录下来
            LogUtils.exportAndLogRecordType(exporter, outputStream, new ReportRepositoryDeal(req, sessionIDInfor), recordType);
        } catch (Exception e) {
            ExportUtils.resetContent(res);
            ErrorHandlerHelper.getErrorHandler().error(req, res, e);
            FineLoggerFactory.getLogger().error(e.getMessage(), e);
        }
        try {
            outputStream.flush();
            outputStream.close();
        } catch (IOException ignored) {
        }
    }
}

四、支持版本

产品线

版本

支持情况

备注

FR8.0支持
FR9.0支持
FR10.0支持
BI3.6支持
BI4.0支持
BI5.1支持
BI5.1.2支持
BI5.1.3支持

五、插件注册

Code Block
languagexml
themeEclipse
firstline1
titleplugin.xml
linenumberstrue
<extra-report>
        <ExportOperateProvider class="your class name"/>
</extra-report>


六、原理说明

ExportFactory为所有导出类型的一个工厂类,通过format参数进行指定。工厂类加载时会注册所有插件中声明的新导出类型到OPERATE_MAP<String, Operate>对象中。实际导出服务被调用时,根据format参数,再创建具体的导出类型操作器进行导出。

七、特殊限制说明

OPERATE_MAP<String, Operate> 注册插件中的实例是在加载产品内置的导出类型之后的,而且是直接按format设置的,所以该接口在特殊情况下也可以用于替换原来的某些导出操作器的特性。只要markType接口返回值与需要替换的导出操作器的类型一致即可。

注:如果是作为替换产品固有的导出操作器使用时,该插件不得上架到商城插件!否则容易引发冲突!markType接口返回值就是前端format参数的值。

实际的开发过程中,为了具有较好的兼容性,开发者不要直接实现Operate接口,应继承BaseOperate类进行具体的接口实现。 

BaseOperate#setContent接口主要用于实现在响应头中声明文件类型、编码等信息。【点击看例子

在实现ExportCollection时,首先需要声明一个实际的导出对象AppExporter,真正的把报表转换成对应的文件流就在AppExporter对象中进行。【点击看例子

同时在实现ExportCollection时,需要额外声明一个该导出操作行为的埋点类型RecordType,如果开发者开发的导出是全新的类型,则可以自己实现该接口。如果只是对产品本身的某种导出进行覆盖,则可以引用DeclareRecordType中的相关类型。

注:RecordType中的getTypeMark接口返回值,只能使用20000以后的值,以避免跟产品本身的埋点产生冲突。getTypeString接口返回值必须使用 plugin_ 开头。getProductType接口默认返回空即可。【点击看例子

八、常用链接

demo地址:demo-export-operate-provider

九、开源案例

免责声明:所有文档中的开源示例,均为开发者自行开发并提供。仅用于参考和学习使用,开发者和官方均无义务对开源案例所涉及的所有成果进行教学和指导。若作为商用一切后果责任由使用者自行承担。

demo-export-xml