数据库“附件(数据库)”迁移至文件服务器示例代码原创
金蝶云社区-Running
Running
3人赞赏了该文章 2,548次浏览 未经作者许可,禁止转载编辑于2021年12月14日 16:49:17
summary-icon摘要由AI智能服务提供

本文简要介绍了如何将存储在数据库中的物理文件(附件)迁移至文件服务器中,以缓解数据库存储压力。文本说明了附件字段如何以Base64字符串形式存储文件信息,并通过示例代码展示了如何遍历这些文件信息,将其从Base64字符串转换为二进制数据并上传到文件服务器。示例代码以采购订单单据(单据编号为CGDD000401)为例,展示了如何筛选单据数据、转换并上传文件,并在文件服务器成功上传后保存附件信息到数据库。此外,还提到了迁移过程中需注意的事项,如分批迁移、迁移后的数据清理等。

本示例演示将附件(数据库)里的物理文件,迁移至附件菜单的附件列表下。


附件(数据库)】属于常规字段,物理文件存储于数据库表格字段下,单据中可放表头表体,用以上传并存储 UploadWhiteList白名单允许的文件,通过设置允许多选属性可批量上传多个文件,其中这多个文件的文件名、文件大小以及文件内容等信息由 JSONArray 数组对象转 Base64 字符串后直接存储于数据库字段下,数据实体对象的属性类型为字符串类型。

当单据附件上传量过大时,容易造成数据库越来越大,现提供个简单的示例代码用以将“上传文件字段”下的附件迁移至文件服务器中。本例以采购订单单据编号为CGDD000401的单据为例(可根据实际需要自定义自己的过滤条件,建议是分批次进行迁移),对单据头上的“F_FF_FILEUPDATE 附件(数据库)”进行迁移,迁移前需配置并启用文件服务器。

备注:早期版本字段名称为“上传文件字段”,后改名为“附件(数据库)”。


161956rgy5wwk570fg2erk.jpg


示例代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Kingdee.BOS;
using Kingdee.BOS.Core.DynamicForm.PlugIn;
using Kingdee.BOS.Core.DynamicForm.PlugIn.Args;
using Kingdee.BOS.Core.Metadata;
using Kingdee.BOS.Core.SqlBuilder;
using Kingdee.BOS.JSON;
using Kingdee.BOS.Orm.DataEntity;
using Kingdee.BOS.ServiceHelper;
using Kingdee.BOS.Util;
using Kingdee.BOS.Core;
using Kingdee.BOS.FileServer.Core;
using Kingdee.BOS.FileServer.Core.Object;
using Kingdee.BOS.FileServer.Core.Object.Enum;
using Kingdee.BOS.FileServer.ProxyService;
using Kingdee.BOS.Log;

namespace Running.Sample.PlugIn.BusinessPlugIn.DynamicForm
{
    /// <summary>
    /// “上传文件字段”迁移至文件服务器示例代码。
    /// </summary>
    [Description("“上传文件字段”迁移至文件服务器示例代码。")]
    public class P20190307TransferFileUpdateFieldPlugIn : AbstractDynamicFormPlugIn
    {
        private const string BillFormId = "PUR_PurchaseOrder"; //待迁移业务对象唯一标志。
        private const string FilterString = "FBILLNO = 'CGDD000401'"; //待迁移单据数据筛选条件。
        /// <summary>
        /// 待迁移单据的单据主键、单据编号以及文件上传字段的字段名。
        /// </summary>
        private readonly string[] _selectFieldKeys = new string[] { "FID", "FBILLNO", "F_FF_FILEUPDATE" };
        private FormMetadata _attachmentMetadata; //附件明细元数据对象。

        public override void BarItemClick(BarItemClickEventArgs e)
        {
            _attachmentMetadata = MetaDataServiceHelper.Load(this.View.Context, "BOS_Attachment") as FormMetadata;
            Debug.Assert(_attachmentMetadata != null, "_attachmentMetadata != null");

            //根据业务对象唯一标志、筛选条件及字段列表,返回待迁移的附件数据,并逐条开始遍历迁移。
            QueryBuilderParemeter queryParameter = new QueryBuilderParemeter
            {
                FormId = BillFormId,
                SelectItems = SelectorItemInfo.CreateItems(_selectFieldKeys),
                //TODO: 自行定义筛选策略,建议分批筛选进行迁移,本次以采购订单单据编号为CGDD000401的单据为例。
                FilterClauseWihtKey = FilterString
            };
            DynamicObjectCollection billDynObjCol = QueryServiceHelper.GetDynamicObjectCollection(this.View.Context, queryParameter);
            foreach (DynamicObject billDynObj in billDynObjCol)
            {
                try
                {
                    //逐条遍历单据数据,进行迁移。
                    TransferFilesByBillDynObj(billDynObj);
                }
                catch (Exception ex)
                {
                    Logger.Error("TransferFileUpdateField",
                        string.Format("FormId:{0}, FID:{1}的文件迁移失败。", BillFormId, billDynObj["FId"]), ex); /*untrans*/
                }
            }
        }

        /// <summary>
        /// 传入单据实体数据对象,对其下上传文件字段进行迁移。
        /// </summary>
        /// <param name="billDynObj">单据实体数据对象。</param>
        private void TransferFilesByBillDynObj(DynamicObject billDynObj)
        {
            //将文件信息由Base64字符串转为Json数组,因为一个字段值下可能同时挂多个附件,所以需对单个字段值下多个附件逐个遍历上传。
            string filesInfo = ObjectUtils.Object2String(billDynObj["F_ff_FileUpdate"]);
            if (filesInfo.IsNullOrEmptyOrWhiteSpace()) return;
            JSONArray filesObj = SerializatonUtil.DeserializeFromBase64<JSONArray>(filesInfo);
            if (filesObj == null || filesObj.Count <= 0) return;
            //逐个遍历附件,获取文件字节数组并转为内存流,逐个上传至文件服务器。
            List<DynamicObject> attachmentDataList = new List<DynamicObject>();
            foreach (object fileObj in filesObj)
            {
                JSONObject fileJsonObj = (JSONObject)fileObj;
                if (fileJsonObj["FileName"].IsNullOrEmptyOrWhiteSpace() || fileJsonObj["FileContent"] == null) continue;

                MemoryStream fileContentStream = new MemoryStream((byte[])fileJsonObj["FileContent"]);
                TFileInfo tFileInfo = new TFileInfo()
                {
                    CTX = this.View.Context,
                    FileId = string.Empty,
                    FileName = fileJsonObj["FileName"].ToString(),
                    Last = true,
                    Stream = fileContentStream
                };
                FileUploadResult uploadRes = new UpDownloadService().UploadAttachment(tFileInfo);
                Logger.Info("TransferFileUpdateField",
                    string.Format("FFileId:{0}, Success:{1}, Msg:{2}", uploadRes.FileId, uploadRes.Success, uploadRes.Message));
                if (!uploadRes.Success) continue;

                //判断上传成功后,收集附件数据,准备批量保存进附件信息表。
                attachmentDataList.Add(CollectAttachmentData(billDynObj, uploadRes));
            }
            if (attachmentDataList.Count > 0)
                BusinessDataServiceHelper.Save(this.View.Context, attachmentDataList.ToArray());
            //TODO: 上传成功后是否直接删除数据库文件,需自行定义策略,建议等人工确认附件迁移成功删除历史附件。
        }

        private DynamicObject CollectAttachmentData(DynamicObject billDynObj, FileUploadResult uploadRes)
        {
            DynamicObject attachmentData = new DynamicObject(_attachmentMetadata.BusinessInfo.GetDynamicObjectType());
            attachmentData["BillType"] = BillFormId; //单据唯一标志。
            attachmentData["InterID"] = billDynObj["FId"]; //单据主键。
            attachmentData["EntryKey"] = " "; //单据体实体标志,若附件挂单据头上,此处赋值为空即可。
            attachmentData["AttachmentName"] = uploadRes.FileName; //文件名。
            attachmentData["BillNo"] = billDynObj["FBillNo"]; //单据编号。
            attachmentData["AttachmentSize"] = Math.Round(Convert.ToDecimal(uploadRes.FileSize) / 1024, 2); //文件大小,KB为单位。
            attachmentData["CreateTime"] = DateTime.Now; //创建时间。
            attachmentData["FBillStatus"] = "A"; //单据状态(无效字段)。
            attachmentData["CreateMen_Id"] = this.View.Context.UserId;
            attachmentData["EntryInterID"] = "-1"; //单据体内码。
            attachmentData["ExtName"] = Path.GetExtension(uploadRes.FileName); //文件后缀。
            attachmentData["FileId"] = uploadRes.FileId; //文件内码。
            attachmentData["FileStorage"] = "1"; //文件服务器存储方式。
            attachmentData["IsAllowDownLoad"] = 0; //是否禁止下载。
            return attachmentData;
        }
    }
}

图标赞 3
3人点赞
还没有人点赞,快来当第一个点赞的人吧!
图标打赏
0人打赏
还没有人打赏,快来当第一个打赏的人吧!