一、需求分析

公司需要将存在于旧系统(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. MyBatis对于Java对象里的枚举类型处理

    平时咱们写程序实体类内或多或少都会有枚举类型属性,方便嘛.但是mybatis里怎么处理他们的增删改查呢? 要求: 插入的时候,会用枚举的定义插入数据库,我们希望在数据库中看到的是数字或者其他东西: 查 ...

  2. SpringBoot引入freemaker前端模板

    1.引入freeMarker的依赖包 <!-- 引入freeMarker的依赖包. --> <dependency> <groupId>org.springfram ...

  3. Java线程池的使用以及原理

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6543909.html  一:线程池简介 在使用多线程来提高处理器利用率的同时,由于线程的不断创建和销毁所造成的 ...

  4. django之创建第7-6-第三种传值方式

    1.创建bar.html文件 <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...

  5. JUC-线程池调度-ScheduledThreadPool

    线程调度使用类:ScheduledExecutorService 创建线程池调度类对象: ScheduledExecutorService pool = Executors.newScheduledT ...

  6. nmake构建Geos库

    1.下载源码包 下载地址 http://download.osgeo.org/geos/geos-3.6.1.tar.bz2 下载之后解压即可. 2.编译 geos源码包中自带了makefile.vc ...

  7. 微软微服务架构eShopOnContainers

    为了推广.Net Core,微软为我们提供了一个开源Demo-eShopOnContainers,这是一个使用Net Core框架开发的,跨平台(几乎涵盖了所有平台,windows.mac.linux ...

  8. Ubuntu 安装asciidoc时默认推荐了巨大的安装包

    $ sudo apt-get install asciidocReading package lists... DoneBuilding dependency tree       Reading s ...

  9. 转:Ogre的SceneManager分析

    SceneManager分析 场景管理主要工作包括以下几点: 1.可移动.不可移动和可渲染物体的创建删除. 2.场景查询. 3.渲染队列. 4.动态阴影. 一. 场景对象创建 场景中的所有对象,包括可 ...

  10. windows7下安装apache+PHP5.3

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