一、需求分析

公司需要将存在于旧系统(TFS)所有的文档迁移至新系统(SharePoint 2013)。现已经将50G以上的文档拷贝到SharePoint 2013 Server上。这些文档是一些不规则的资料,除了常见的Office文件、PDF,还包括图片、RAR等,甚至还包括一些快捷方式(.link)这类的"脏数据"。除此之外,这些存在于TFS中的文档,名称也是"不规则",即包含了SharePoint 2013文档命名不支持的字符如"&", "\"", "?", "<", ">", "#", "{", "}", "%", "~", "/", "\\"。所以,这对导入又增加了复杂度。

了解了文档内容和命名规则后,接下来就是分析怎样导入至SharePoint文档库中:

  • 首先,每一个二级文件夹的命名是有规则的,正好是项目编号(Project Number),如GCP-xxxx-xxx-xxx。再根据此编号创建一个子站点。
  • 值得一提的是,根据编号创建的子站点并不是随意创建的,而是需要考虑究竟要在哪一个Site Collection下创建子站点,并且还要给予独立权限的分配,即为子站点打断权限继承,为其增加两个组(Owners和Members),并向组里添加对应的人员。
  • 对应的创建规则存在于如下List中

其中Project Number即项目编号,与TFS中文件夹的名称一致。Department 即需要将此子站点创建于哪个Site Collection中,包含两个值SMO和CO。PM列是一个Person Or Group类型的字段,需要将此字段的值加入到Owner组,Domain Group列也是一个Person Or Group类型的字段,需要将此字段的值加入到Member组中。

  • 接下来,是最重要的一步,找到最佳实践去创建各个Level的文件夹并传入文档。

二、分析和构建导入程序

首先,文件夹的目录结构如下图所示:

文档目录结构图

  • 根据上图文档目录结构图,分割字符串(E:\TFS\GCP0401-S\4.Project Management\3 Document Management\TMF),获取文件夹的名称,即Project Number(GCP0401-S)。然后根据此Project Number找到对应的Department、PM、Domain Group,最后创建子站点。逻辑代码如下图所示:
private SPWeb CreateSubSite(string webUrl, string webTitle, string description)
{
var result = (from e in projectInfos where e.ProjectNumber == webUrl && tempArray.Contains(webUrl) select e).FirstOrDefault();
if (result==null)
{
logger.Debug("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&以下Sub Site没有创建********");
logger.Debug("Web Url="+webUrl);
logger.Debug("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*********");
return null;
}
logger.Debug("开始创建在Site Collection:"+result.Department.ToUpper());
using (SPSite site = result.Department.ToUpper()=="SMO"
? new SPSite("http://sp/sites/smo")
:new SPSite("http://sp/sites/cro"))
{//{*/using(SPSite site=new SPSite("http://reus")){
using (SPWeb web = site.OpenWeb())
{
try
{
SPWeb subWeb = null;
if (site.AllWebs[webUrl].Exists)
{
subWeb = site.AllWebs[webUrl];
}
else
{ logger.Debug("不存在"+webUrl+",则创建新的WebSite");
//不存在则创建新的WebSite
subWeb = site.AllWebs.Add(webUrl, webTitle, description, 1033, "STS#0", true, false); string groupTitleForOwners = subWeb.Title + " Owners";
subWeb.AssociatedOwnerGroup = EnsureGroup(subWeb,groupTitleForOwners , "Full Control"); string groupTitleForMembers = subWeb.Title + " Members";
subWeb.AssociatedMemberGroup = EnsureGroup(subWeb,groupTitleForMembers , "Edit"); subWeb.Groups[groupTitleForOwners].AddUser(subWeb.Site.Owner); if (result.PM!=null)
{
subWeb.Groups[groupTitleForOwners].AddUser(result.PM);
}
if (result.DomainGroup!=null)
{
subWeb.Groups[groupTitleForMembers].AddUser(result.DomainGroup);
} subWeb.Update();
logger.Debug(webUrl+"创建成功");
richTextBox2.Text += webUrl + "创建OK" + "\r\n";
} return subWeb;
}
catch (Exception es)
{
logger.Debug(es.ToString());
return null;
} }
} }
  • 从上文档目录结构图可以分析出,二级目录是项目编号,即对应要创建的子站点。在此目录下有"无限级"的子文件夹。那应该怎样在子站点的文档库中创建如此多的文件夹呢,这需要好好考虑一下。对,用递归,得到每一个分支最底层的文件夹路径即可。具体实现如下所示:
private void GetDeepestFoleder(string sDir)
{
string dir = string.Empty;
try
{
foreach (string path in Directory.GetDirectories(sDir))
{
dir = path; if (!arrayList.Contains(dir))
{
arrayList.Add(dir);
} arrayList.Remove(dir.Substring(, dir.LastIndexOf("\\"))); GetDeepestFoleder(dir);
}
}
catch (Exception excpt)
{
logger.Debug(excpt.ToString());
}
}
  • 得到所有最内层文件夹的URL之后,接着就是在SharePoint 文档库中创建一级一级的文件夹了。
 private void CreateForderForDocumentLibrary(string folderUrl, SPWeb currentWeb)
{
try
{
SPFolder newFolder = null;
string spFolderUrl = currentWeb.ServerRelativeUrl + "/Shared Documents";
logger.Debug("SPFolder Url="+spFolderUrl);
//分割字符串,得到父子Folder的Url,在文档库中创建文件夹
foreach (string strUrl in folderUrl.Split('\\'))
{ //todo:有空格会报错吗?
string tempStrUrl = strUrl.Trim();
//SharePoint 文档库中文件名有严格的格式要求
var r = new[] { "&", "\"", "?", "<", ">", "#", "{", "}", "%", "~", "/", "\\"}.Any(tempStrUrl.Contains);
if (r)
{
tempStrUrl = tempStrUrl.Clean();
} if (tempStrUrl.StartsWith(".") || tempStrUrl.EndsWith("."))
{
tempStrUrl = tempStrUrl.Replace(".", "-");
} spFolderUrl += "/" + tempStrUrl;
logger.Debug("SPFolder Url="+spFolderUrl);
SPList list = currentWeb.Lists.TryGetList("Documents");
SPFolderCollection folderCollection = list.RootFolder.SubFolders;
newFolder = folderCollection.Add(spFolderUrl);
logger.Debug(spFolderUrl + "创建成功");
}
}
catch (Exception ex)
{
logger.Debug(ex.ToString());
throw;
}
}
  • 以上代码逻辑中包含了字符串的处理,因为SharePoint 2013的文档、文件夹命名有严格的要求,不能包含非法字符。并且也不能以字符 "."开始或者结束。所以添加了字符串处理操作功能。
 public static class MyStringExtention
{
public static string Clean(this string s)
{
StringBuilder sb = new StringBuilder(s);
//"&", "\"", "?", "<", ">", "#", "{", "}", "%", "~", "/", "\\" uu.uu可以,但uu.就不行
sb.Replace("&", "-");
sb.Replace("\"", "-");
sb.Replace("?", "-");
sb.Replace("<", "-");
sb.Replace(">", "-");
sb.Replace("#", "-");
sb.Replace("{", "-");
sb.Replace("}", "-");
sb.Replace("%", "-");
sb.Replace("~", "-");
sb.Replace("/", "-");
sb.Replace("\\", "-");
// sb.Replace(".", "-"); return sb.ToString();
}
}
  • 在成功创建了子站点并在文档库中创建了所有文件夹后,接下来就是将文档上传至指定的文件夹中了。所以接下来,需要获取指定目录下所有的文件,我使用了一个队列来保存文件路径,而不是使用递归或者使用.NET 4.0提供的基于文件迭代的功能(Directory.EnumerateFiles)来获取所有文件,原因有2点:
    • Directory.EnumerateFiles内置的递归方法容易抛出异常,比如没有权限访问等。
    • Queue<String> 避免了多次递归时调用堆栈从而会创建大数组。
 private IEnumerable<string> GetFiles(string path)
{
Queue<string> queue = new Queue<string>();
queue.Enqueue(path);
while (queue.Count > )
{
path = queue.Dequeue();
try
{
foreach (string subDir in Directory.GetDirectories(path))
{
queue.Enqueue(subDir);
}
}
catch (Exception ex)
{
logger.Debug("===========发生错误啦*========================");
logger.Debug(ex.ToString());
logger.Debug("===========发生错误了========================");
}
string[] files = null;
try
{
files = Directory.GetFiles(path);
}
catch (Exception ex)
{
logger.Debug("===========发生错误啦&========================");
logger.Debug(ex.ToString());
logger.Debug("===========发生错误了========================");
}
if (files != null)
{
for (int i = ; i < files.Length; i++)
{
yield return files[i];
}
}
}
}
  • 在获取了所有文件之后,上传至指定文档库即可,别忘记处理非法字符。
private void UploadFileToDocumentLibrary(string folderUrl, string filePath, SPWeb currentWeb)
{ try
{
//todo:不同文件名字相同会覆盖吗todo
SPFolder newFolder = null;
string spFolderUrl = currentWeb.ServerRelativeUrl+ "/Shared Documents";
//极端情况 GCP0117 TFS List.xls
//分割字符串,得到父子Folder的Url,在文档库中创建文件夹
foreach (string strUrl in folderUrl.Split('\\'))
{ //todo:有空格会报错吗?
string tempStrUrl = strUrl.Trim();
//SharePoint 文档库中文件名有严格的格式要求
var result = new[] { "&", "\"", "?", "<", ">", "#", "{", "}", "%", "~", "/", "\\" }.Any(tempStrUrl.Contains);
if (result)
{
tempStrUrl = tempStrUrl.Clean();
} if (tempStrUrl.StartsWith(".") || tempStrUrl.EndsWith("."))
{
tempStrUrl = tempStrUrl.Replace(".", "-");
} spFolderUrl += "/" + tempStrUrl;
}
newFolder = currentWeb.GetFolder(spFolderUrl); string fileName = System.IO.Path.GetFileName(filePath);
//SharePoint 文档库中文件名有严格的格式要求
var r=new[] { "&", "\"", "?", "<", ">", "#", "{", "}", "%", "~", "/", "\\"}.Any(fileName.Contains);
if (r)
{
logger.Debug("***********File Name包含了Invalid Value***********************");
logger.Debug("***********File Name="+fileName);
logger.Debug("***********File Path="+filePath); fileName = fileName.Clean();
logger.Debug("*********替换过后的File Name************************************");
logger.Debug(fileName);
} if (fileName.StartsWith(".") || fileName.EndsWith("."))
{
fileName=fileName.Replace(".", "-");
} //判断文件是否已经存在,若存在,则不再重复上传 string spFileUrl = spFolderUrl + "/" + fileName;
SPFile newSpFile = currentWeb.GetFile(spFileUrl);
if (!newSpFile.Exists)
{
using (FileStream fs = File.OpenRead(filePath))
{
byte[] contentBytes = new byte[fs.Length];
fs.Read(contentBytes, 0, (int)fs.Length);
if (newFolder != null)
{
SPFile spFile = newFolder.Files.Add(fileName, contentBytes, true);
newFolder.Update();
} }
logger.Debug(fileName + "上传成功 and FilePath=" + filePath);
}
else
{
logger.Debug(spFileUrl+"已存在");
} }
catch (Exception ex)
{
logger.Debug(ex.ToString());
throw;
}
}

三、异常处理

主要发生的异常是文件名包含Invalid字符,对SharePoint而言,文档库Folder和File的名字都有严格的限制,不能包含#、%等,现在处理异常是记录到日志然后手动去修改名称

  • 报错的异常

  • 将异常记录至日志里,方便修改。

四、检查是否导入成功

  • 导入成功界面

  • 检查日志

  • 登陆系统,检查是否全部导入,并且检查权限设置是否正确。

  • 查看文件夹和文档是否成功创建和上传

迁移TFS,批量将文档导入SharePoint 2013 文档库的更多相关文章

  1. sharepoint 2013 列表和库标签 元数据导航配置(2)

    接前面提到的,如何创建一个术语库.sharepoint 2013 列表和库标签 元数据导航配置(1), 现在要做的,就是如何在自定义或者文档库中使用这个术语库,实现标签功能,通过这些标签,找到对应的文 ...

  2. SharePoint 2013 文档库中PPT转换PDF

    通过使用 PowerPoint Automation Services,可以从 PowerPoint 二进制文件格式 (.ppt) 和 PowerPoint Open XML 文件格式 (.pptx) ...

  3. SharePoint 2013 文档上传的多种形式

    SharePoint 2013 中的某些功能需要使用 ActiveX 控件.这会在不支持 ActiveX 的浏览器上产生限制.目前只有 32 位版本的 Internet Explorer 支持此功能. ...

  4. sharepoint 2013 文档库 资源管理器打开报错 在文件资源管理器中打开此位置时遇到问题,将此网站添加到受信任站点列表,然后重试。

    我们在使用sharepoint 2013的文档库或者资源库的时候,经常会需要用到使用“资源管理器”来管理文档,但是有时候,点击“使用资源管理器打开”,会提示如下错误: 在文件资源管理器中打开此位置时遇 ...

  5. sharepoint 2013 文档库eventhandle权限控制

    记录一下如何在sharepoint server 2013文档库中,使用eventhandle控制文档库document library的条目item权限. ///<summary> // ...

  6. SharePoint 2013 文档库“样式”变了

    有朋友反馈说文档库的样式变了. 经查证,原来有人修改了视图的"样式":库设置—视图—样式,改为默认即可. 另外,如果编辑页面,编辑web部件的属性,在"杂项"勾 ...

  7. 启用SharePoint 2013文档版本控制

    cls $PSSnapin = Add-PsSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue | Out-Nul ...

  8. 跟我学SharePoint 2013视频培训课程——签出、签入文档(9)

    课程简介 第9天,怎样在SharePoint 2013中签出.签入文档 视频 SharePoint 2013 交流群 41032413

  9. SharePoint 2013 入门教程

    以下文章是自己在学习SharePoint的过程中,不断积累和总结的博文,现在总结一个目录,分享给大家.这个博客也是自己从SharePoint入门,到一个SharePoint开发的成长记录,里面记录的都 ...

随机推荐

  1. Jackcard类似度和余弦类似度(向量空间模型)的java实现

    版权声明:本文为博主原创文章,地址:http://blog.csdn.net/napoay,转载请留言. 总结Jackcard类似度和余弦类似度. 一.集合的Jackcard类似度 1.1Jackca ...

  2. 【高德地图Android SDK】视频教学

    前两天参加了高德在北航举办的公开课,感觉非常不错.完成老师布置的作业之后,还顺利地拿到了高德开发者认证证书!! 现在来跟大家分享一下,如何快速学习[高德地图Android SDK]的开发.一天包会!连 ...

  3. Android 之 获取地理位置及监听

    第一步.添加权限 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> ...

  4. ubuntu 下 cajview 替代方案

    .caj 是知网提供的论文的标准格式,但是,知网只提供 win 版的工具.这里尝试了两个两个方案,均可行,做一下记录在此. 1. 使用 wine 版的 cajview  pdf 可以正常用,但是 ca ...

  5. cocos2d-js 免安装在线版 粒子编辑器 particle editor particle builder 兼容pex和plist

    http://onebyonedesign.com/flash/particleeditor/ 这个原来是为flash starling设计的粒子系统编辑器,但实际上,还是能兼容cocos2d的. 只 ...

  6. Flash XSS 漏洞详解 根治的好办法

    本文的目的是深层次的分析Flash的ExternalInterface的XSS漏洞,并提出比较有效的解决方案.   首先,我们看看什么情况下,会出现XSS.   第一种情况:        把flas ...

  7. P2093 零件分组【贪心算法练习题】

    题目链接: http://codevs.cn/problem/4888/ https://www.luogu.org/problem/show?pid=2093 题目描述 某工厂生产一批棍状零件,每个 ...

  8. ajax done和always区别

    jQuery中Ajax有done和always这两个回调方法:done:成功时执行,异常时不会执行.always:不论成功与否都会执行.

  9. windows7下安装apache+PHP5.3

    Apache+PHP 安装与配置 最近在学习PHP,所以就在windows7下安装了PHP的开发环境.之所以没有选择集成的软件,如WAMP.AppServ等套件,是为了单独安装这些软件,有助于更加深入 ...

  10. ios判断是否有中文

    //判断是否有中文 -(BOOL)IsChinese:(NSString *)str { ; i< [str length];i++){ int a = [str characterAtInde ...