我被要求为我工作的组织编写一个文档管理系统,它提供了与不同记录相关的一系列九个不同的工作流程。工作流程包括将文档添加到“文件”或记录,并根据业务规则将这些文档的子集发布到公共网站。
文档几乎无一例外都是 PDF 格式,并且在任何时候,对于任何一条记录,处理的文件通常少于 20 个。
将其构建为 Web 应用程序的一个主要原因是将文件保存在我们的数据中心和高速交换机上,而不是尝试通过远程站点的可能较慢的连接速度在位置之间进行上下复制。
该系统运行完美,直到大量文档(114 个 PDF 文档,总大小为 329MB)在 95% 左右超时。
代码是(IncomingDocuments 的类型是 List<FileInfo>)-
List<string> filesSuccessfullyAdded = new List<string>();
foreach (FileInfo incomingFile in IncomingDocuments)
{
FileOperations.AddDocument(incomingFile, false, ApplicationCode, (targetDirectoryPath.EndsWith(@"\") ? targetDirectoryPath : targetDirectoryPath + @"\"));
FileInfo copiedDocument = new FileInfo(Path.Combine(targetDirectoryPath, incomingFile.Name));
if (copiedDocument.Exists && copiedDocument.Length == incomingFile.Length && copiedDocument.LastWriteTime == incomingFile.LastWriteTime)
{
filesSuccessfullyAdded.Add(copiedDocument.Name);
}
}
if (filesSuccessfullyAdded.Any())
{
SetupConfirmationLiteral.Text += "<p class='info'>The following files have been successfully added to the application file-</p>";
XDocument successfullyAddedList = new XDocument(
new XElement("ul", new XAttribute("class", "documentList")));
foreach (string successfulFile in filesSuccessfullyAdded)
{
successfullyAddedList.Root.Add(new XElement("li", successfulFile));
}
SetupConfirmationLiteral.Text += successfullyAddedList.ToString();
}
var notSuccessfullyAdded = from FileInfo incomingDocument in IncomingDocuments
where !filesSuccessfullyAdded.Contains(incomingDocument.Name)
orderby incomingDocument.Name ascending
select incomingDocument.Name;
if (notSuccessfullyAdded.Any())
{
SetupConfirmationLiteral.Text += "<p class='alert'>The following files have <strong>not</strong> been successfully added to the application file-</p>";
XDocument notAddedList = new XDocument(
new XElement("ul", new XAttribute("class", "documentList")));
foreach (string notAdded in notSuccessfullyAdded)
{
notAddedList.Root.Add(new XElement("li", notAdded));
}
SetupConfirmationLiteral.Text += notAddedList.ToString();
SetupConfirmationLiteral.Text += "<p>A file of the same name may already exist in the target location.</p>";
}
使用实用方法-
public static void AddDocument(FileInfo sourceFile, bool appendName, string applicationCode, string targetPath)
{
try
{
DirectoryInfo targetDirectory = new DirectoryInfo(targetPath);
if (targetDirectory.Exists)
{
string targetFileName = (appendName ? sourceFile.Name.Insert(sourceFile.Name.IndexOf(sourceFile.Extension, StringComparison.Ordinal), " UPDATED") : sourceFile.Name);
if (targetDirectory.GetFiles(targetFileName).Any())
{
//Do not throw an exception if the file already exists. Silently return. If the file exists and matches both last modified and size it won't be reported, and can be archived as normal,
//otherwise it should be reported to user in the calling method.
return;
}
string targetFileUnc = Path.Combine(targetPath, targetFileName);
sourceFile.CopyTo(targetFileUnc, overwrite: false);
Logging.FileLogEntry(username: (HttpContext.Current.User.Identity.IsAuthenticated ? HttpContext.Current.User.Identity.Name : "Unknown User"), eventType: LogEventType.AddedDocument,
applicationCode: applicationCode, document: sourceFile.Name, uncPath: targetFileUnc);
}
else
{
throw new PdmsException("Target directory does not exist");
}
}
catch (UnauthorizedAccessException ex)
{
throw new PdmsException("Access was denied to the target directory. Contact the Service Desk.", ex);
}
catch (PathTooLongException)
{
throw new PdmsException(string.Format("Cannot add document {0} to the Site File directory for Application {1} - the combined path is too long. Use the Add Documents workflow to re-add documents to this Site File after renaming {0} to a shorter name.", sourceFile.Name, applicationCode ));
}
catch (FileNotFoundException ex)
{
throw new PdmsException("The incoming file was not found. It may have already been added to the application file.", ex);
}
catch (DirectoryNotFoundException ex)
{
throw new PdmsException("The source or the target directory were not found. The document(s) may have already been added to the application file.", ex);
}
catch (IOException ex)
{
throw new PdmsException("Error adding files - file(s) may be locked or there may be server or network problem preventing the copy. Contact the Service Desk.", ex);
}
}
进行实际的复制和审核。PdmsException 只是我们用来向用户显示有用的错误消息的特定异常类,允许他们尽可能解决自己的问题,或者至少给出一个可以理解的失败原因。
我知道我可以简单地将 ExecutionTimeout 属性(http://msdn.microsoft.com/en-us/library/system.web.configuration.httpruntimesection.executiontimeout.aspx)增加到默认的 110 秒以上 - 最多说 300 秒- 这可能意味着在这种情况下超时停止发生,但是如果用户试图添加或发布数千个文档怎么办。这个解决方案不能很好地扩展,只是推迟而不是解决问题。
我正在将 .NET 4 与 Visual Studio 2010 一起使用,据我所知,如果我们想要打包使用 ajax 记录和更新进度。为了使用 Microsoft 提供的异步目标包,我无法访问 Visual Studio 2012 甚至比 XP 更新的 Windows。
鉴于这些限制,有没有办法打包/批量处理这些文档以避免超时并(理想情况下)在添加每个批次时向用户提供反馈?如果 F# 易于实现,我愿意探索它。或者,我应该为 Visual Studio 2012 辩护吗?