来源于:http://www.cnblogs.com/jianyus/p/3262613.htmlhtml
前言:说来惭愧,咱们的SharePoint内网门户跑了2年,不堪重负,数据量也不是很大,库有60GB左右,数据量几万条,总之因为各类缘由吧,网站速度很是慢,具体问题研究了好久,也无从解决,全部考虑用Net从新搭网站,进行数据迁移,也就带来了数据迁移这个问题。web
思路:因为SharePoint的架构和Net有着不同的特色,并且SharePoint的数据库设计是不为人所知的(固然咱们能够了解一些,但不彻底),虽然也是基于Net架构的,可是咱们很难作到Sql To Sql的方式。因此,只能考虑服务器端对象模型,插入到数据库中的方式,其间,经理给的建议很是合理,就是将SharePoint的数据整理好插入中间库,而后统一插入到新网站数据库中。在后来的实践中,发现这一方法对数据迁移和检查,都有着很是好的帮助,避免了不少SharePoint对象模型中出错,可是很差更正的现象。正则表达式
中间库设计:数据库
考虑到原内网门户有列表、文档库、图片库三种主要类型(特殊列表特殊对待),因此建立了两个数据库表,分别用来存List和DocLib,同时再建立两个表Image和Attachment用来存列表正文中的图片和列表附件(文档库文档当作列表附件)。服务器
1、用来存储列表内容的表 -- TABLE [dbo].[List]架构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
[ID] [
int
] IDENTITY(1,1) NOT NULL,--主键ID
[WebID] [nvarchar](max) NULL,--所在网站的Guid
[ListID] [nvarchar](max) NULL,--所在列表的Guid
[ListName] [nvarchar](max) NULL,--列表名称
[ContentType] [nvarchar](max) NULL,--所属内容类型
[ItemID] [nvarchar](max) NULL,-- 列表里面的ID
[ApprovalState] [
int
] NULL,--审批状态
[Title] [nvarchar](max) NULL,--标题
[SubTitle] [nchar](10) NULL,--副标题
[ItemContent] [nvarchar](max) NULL,--内容
[Creator] [nvarchar](max) NULL,--建立者LoginName
[CreatorID] [nvarchar](max) NULL,--建立者UserID
[DispCreator] [nvarchar](max) NULL,--建立者UserName
[Modifier] [nvarchar](max) NULL,--修改者LoginName
[ModifierID] [nvarchar](max) NULL,--修改者UserID
[DispModifier] [nvarchar](max) NULL,--修改者UserName
[CreatTime] [datetime] NULL,--建立时间
[ModifyTime] [datetime] NULL,--修改时间
[TransferDate] [datetime] NULL,--数据迁移时间
|
2、用来存储文档库/图片库的表 -- TABLE [dbo].[DocLib]数据库设计
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
[ID] [
int
] IDENTITY(1,1) NOT NULL,--主键ID
[WebID] [nvarchar](max) NULL,--所在网站的Guid
[ListID] [nvarchar](max) NULL,--所在列表的Guid
[ListName] [nvarchar](max) NULL,--列表名称
[ListType] [nvarchar](max) NULL,--列表类型(文档库/图片库)
[ItemID] [nvarchar](max) NULL,-- 列表里面的ID
[ApprovalState] [
int
] NULL,--审批状态
[Title] [nvarchar](max) NULL,--标题
[Creator] [nvarchar](max) NULL,--建立者LoginName
[CreatorID] [nvarchar](max) NULL,--建立者UserID
[DispCreator] [nvarchar](max) NULL,--建立者UserName
[Modifier] [nvarchar](max) NULL,--修改者LoginName
[ModifierID] [nvarchar](max) NULL,--修改者UserID
[DispModifier] [nvarchar](max) NULL,--修改者UserName
[CreatTime] [datetime] NULL,--建立时间
[ModifyTime] [datetime] NULL,--修改时间
[Url] [nvarchar](max) NULL,--文档的Url
[TransferDate] [datetime] NULL,--数据迁移时间
|
3、用来存储正文图片的表 -- TABLE [dbo].[Image]测试
1
2
3
4
5
6
7
|
[ID] [
int
] IDENTITY(1,1) NOT NULL,--主键ID
[WebID] [nvarchar](max) NULL,--所在Web的Guid
[WebSubUrl] [nvarchar](max) NULL,--所在Web的相对WebUrl
[ListID] [nvarchar](max) NULL,--所在列表的Guid
[ListName] [nvarchar](max) NULL,--列表名称
[ItemID] [nvarchar](max) NULL,-- 列表里面的ID
[ImageUrl] [nvarchar](max) NULL,--内容图片的Url,多张图片,逗号分隔
|
4、用来存储附件集的表 -- TABLE [dbo].[Attachment]网站
1
2
3
4
5
6
7
|
[ID] [
int
] IDENTITY(1,1) NOT NULL,--主键ID
[WebID] [nvarchar](max) NULL,--所在Web的Guid
[WebSubUrl] [nvarchar](max) NULL,--所在Web的相对WebUrl
[ListID] [nvarchar](max) NULL,--所在列表的Guid
[ListName] [nvarchar](max) NULL--列表名称
[ItemID] [nvarchar](max) NULL,-- 列表里面的ID
[AttachUrl] [nvarchar](max) NULL,--附件的Url,多个的时候,逗号分隔
|
代码方法段:ui
首先就是对象模型读取列表插入List表,而后是对象模型读取文档库/图片库插入DocLib表,读取字段的代码比较简单,咱们就不过多介绍,就介绍下其间遇到的几个问题,也避免代码太多太繁杂。
问题一:正文乱码
这是一个比较操心的问题,插入数据没有问题,可是到新系统显示,发现好多正文带有雷系”?“之类的东西,这样子确定不行,首先想到RePlace,而后想一想不太靠谱,由于正文里颇有可能有正常的问号,这样会被替换掉。后来想到多是编码问题,后来证明确实是编码问题,将特别的空格处理替换为 便可,处理以下:
1
2
3
4
5
6
|
//Content替换空格为
byte
[] space =
new
byte
[] { 0xc2, 0xa0 };
string
UTFSpace = System.Text.Encoding.GetEncoding(
"UTF-8"
).GetString(space);
Content = Content.Replace(UTFSpace,
" "
);
Content = DeleteHtmlImgTag(Content);
Content = Content.Replace(
"'"
,
"''"
);
|
问题二 处理中途报错
插入过程当中,咱们会出现一些操做异常的状况,可能整个程序要运行4-5个小时,可是4个小时的时候,出现异常了,咱们很恼火,调试也很困难,由于很难去调试问题,即便把断点打在Catch里面,调试也是力不从心的,因此,咱们必须一次成功,不允许中间出差错。这样,我采起了空跑程序(只走对象模型,不插入数据库,由于Insert很慢,并且几乎不报错,错误多数出如今对象模型调用上,各类字段没有、对象为空)和记录错误补录两个方式,来避免这样的问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public
static
void
WriteErrorLog(
string
ErrorMessage)
{
try
{
using
(StreamWriter sw = File.AppendText(
@"log_error "
+ InsertTime.ToString(
"yyyy-MM-dd HHmmss"
) +
".txt"
))
{
sw.WriteLine(ErrorMessage);
sw.Dispose();
}
}
catch
{
}
Console.WriteLine(ErrorMessage);
}
|
问题三 处理中间的小错误
操做过程当中,对于代码编写的可靠性,要求很好,就像上面所说,一个要跑4-5个小时的程序,4个小时的时候报错,咱们基本就属于前功尽弃,由于继续插入是很困难的。因此中间的小问题,对于代码段的可靠性要求,就很是高了。必要的时候,多加一些Try...Catch...可能会对于效率有一点点影响,可是对于整个程序来讲,是很是必要的。
1
2
3
4
5
6
|
if
(!web.Exists){}
//判断web是否存在
list = web.Lists[ListName];
//打开的时候Try一下,避免不存在,ListName最好Trim一下
if
(list.BaseTemplate == SPListTemplateType.Announcements)
//判断list类型
if
(list.Fields.ContainsField(
"SubTitle"
))
//判断是否有SubTitle这个字段
//副标题对象不为空,才赋值,不然赋值为空字符串(下面那行的注释…)
SubTitle = (item[
"SubTitle"
] ==
null
) ?
string
.Empty : item[
"SubTitle"
].ToString();
|
问题四 提取正文中的图片URL
咱们数据迁移过程,正文中会带有图片,这就要求咱们把图片保存下来,迁移过去,而后还要插入到相同的位置。这是个比较让人头疼的问题,首先说下逻辑,读取正文的时候,用正则表达式获取全部的图片(不是绝对路径的要拼成绝对路径),而后插入到Image中间库中,将原来图片的位置,替换为一个图片标志,由于以后咱们还要把图片插入到这里。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/// <summary>
/// 取得HTML中全部图片的 URL。
/// </summary>
/// <param name="sHtmlText">HTML代码</param>
/// <returns>图片的URL列表</returns>
public
static
string
[] GetHtmlImageUrlList(
string
sHtmlText)
{
// 定义正则表达式用来匹配 img 标签
Regex regImg =
new
Regex(
@"<img\b[^<>]*?\bsrc[\s\t\r\n]*=[\s\t\r\n]*[""']?[\s\t\r\n]*(?<imgUrl>[^\s\t\r\n""'<>]*)[^<>]*?/?[\s\t\r\n]*>"
, RegexOptions.IgnoreCase);
// 搜索匹配的字符串
MatchCollection matches = regImg.Matches(sHtmlText);
int
i = 0;
string
[] sUrlList =
new
string
[matches.Count];
// 取得匹配项列表
foreach
(Match match
in
matches)
sUrlList[i++] = match.Groups[
"imgUrl"
].Value;
return
sUrlList;
}
|
问题五 将正文中的图片Url换为标识<ImgType>
一样使用正则表达式,将图片标签<img.../>替换为咱们特定的标识,为未来replace回来作准备,代码附下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
/// <summary>
/// 去处HTML中全部图片的img标签。
/// </summary>
/// <param name="sHtmlText">HTML代码</param>
/// <returns>去除img标签后的Html</returns>
public
static
string
DeleteHtmlImgTag(
string
sHtmlText)
{
string
result = Regex.Replace(sHtmlText,
@"<img.*?src=(['""]?)(?<url>[^'"" ]+)(?=\1)[^>]*>"
,
delegate
(Match m)
{
return
"<ImgType>"
;
});
if
(result.IndexOf(
"</img>"
) > 0)
{
result = result.Replace(
"</img>"
,
""
);
}
if
(result.IndexOf(
"</IMG>"
) > 0)
{
result = result.Replace(
"</IMG>"
,
""
);
}
return
result;
}
|
中间库到新系统:
通过将SharePoint中数据,整理插入到中间库的过程,咱们等于已经完成80%的工做,由于剩下的内容,就是Sql To Sql的问题了,对于net开发人员,甚至不须要设计,你只须要了解新系统的数据库结构,相应字段插入就能够了。惟一要提到的就是附件/图片处理的问题,下面我说下个人处理方式:
附件/图片处理
这也是一个比较棘手的问题,由于众所周知的缘由,SharePoint的附件/图片是BLOB的形式,存储在数据库中的(我尝试去数据库中找这个字段,没找到);因此咱们只能用对象模型,固然SPFile是咱们第一时间想到的,可是效率可想而知(效率太慢放弃);因此考虑先将附件/图片的Url地址拼接好,插入到Images/Attachment的中间库中,而后采起WebClient的对象去下载为Byte[],而后直接上传,测试结果仍是很客观的,100个附件1分钟左右(与附件大小有关)。
1
2
3
4
5
6
7
|
using
(WebClient wc =
new
WebClient())
{
NetworkCredential networkCredential =
new
NetworkCredential(
"用户名"
,
"密码"
,
"域"
);
wc.Credentials = networkCredential;
byte
[] ss = wc.DownloadData(url);
return
ss;
}
|
总结:数据迁移过程比较繁杂,须要考虑的东西比较多,前期的规划很重要,由于数据一旦迁移过去,修修补补会很让人郁闷,因此对应关系必定必定要先作好,避免后期修改。并且,两边系统的开发人员对接很是重要,避免出现少插入字段等现象,形成新系统出问题。基本上就是以上这些,写出来给有须要的人们参考下,就这样了。
每天想你红枣,您的健康,‘枣‘的承诺 <a href="http://shop109102900.taobao.com" target="_blank" style="color:red">每天想你红枣淘宝店</a>