【问题标题】:SharePoint Custom Upload Page Using Custom Field Types使用自定义字段类型的 SharePoint 自定义上传页面
【发布时间】:2013-11-15 13:15:40
【问题描述】:

是否可以在 SharePoint 中创建将文件上传控件与自定义字段类型相结合的自定义上传页面,以便用户可以从硬盘驱动器中选择要上传的文件,输入文件标题,可选择添加 cmets,指定内容类型并在多个自定义字段中输入附加数据并创建新的 SPListItem,上传文件并将其关联到新的 SPListItem,最后将所有输入自定义字段的值成功保存到新创建的 SPListItem?

注意:我希望使用 SharePoint 自定义字段类型来执行此任务,而只需使用带有一堆的自定义 ASPX 页面用户控件。

使用自定义字段类型时存在的根本问题是 SharePoint 文档库中的文件上传事件是异步事件。您可以覆盖 SPListItemEventReceiver 中可用的 ItemAdding 方法的默认行为,当文件“正在上传”时,该方法可用于访问某些信息并且您同样可以从 ItemAdded 方法访问有关新创建的 SPListItem 的信息,该方法称为 “在已添加项目之后” - 但由于此方法发生在单独的线程并在不知道与自定义字段或其各自值相关的任何内容的情况下异步执行,因此用户在这些字段中输入的任何数据都不会成功保存。

当用户希望通过使用 EditFormTemplate 编辑自定义字段中的值来更新有关文档的信息时,在初始化期间为每个字段设置 SPListItem 属性。这一切都很好,因为在这种情况下 ListItem 已经存在!问题是,当用户第一次希望上传文档时,ListItem 显然还不存在,因此每个字段都使用设置为“null”的 SPListItem 属性进行初始化,并且将永远保持为 null,因为没有'似乎没有任何方法可以通过引用新创建的 ListItem AFTER文件上传来追溯更新每个字段的 ListItem 属性!

正是出于这个原因,并且仅出于这个原因,微软才坚持强制用户在一个屏幕上上传他们的文件,然后在文件成功上传后将他们重定向到编辑表单。通过拆分这两个页面,Microsoft 强制用户上传文件并创建 ListItem PRIOR 以保存有关文件的任何其他信息。上传文件并创建 ListItem 后,将每个自定义字段的值保存回 ListItem 不会有任何问题因为 ListItem 已经存在

注意: BaseFieldControl 继承自 FieldMetadata,FieldMetadata 继承自 FormComponent。 FormComponent 有一个名为 Item 的属性,它对应于该字段所属的基础 SPListItem。 BaseFieldControl 有一个名为 ListItemFieldValue 的属性,它存储保存回 ListItem 的字段的实际值,它还有一个名为 UpdateFieldValueInItem() 的可覆盖方法,可以使用在将数据分配给 ItemFieldValue 属性之前执行其他逻辑(例如验证)。

UPDATING一个EXISTING SPListItem时,以下代码有效,自定义字段值将被保存因为 SPListItem 已经存在!

var item = MyDocLib.Items[0] as SPListItem;
item["MyCustomFieldName"] = "some value";
item.Update();

在 SPListItemEventReceiver 中,在初始文件上传期间,在创建 ListItem 并且各个自定义字段值“尝试保存”之后,ItemUpdating/ItemUpdated 方法将包含对 SPItemEventProperties properties.ListItem 的空引用,因为如前所述以前,ItemAdded 方法是异步触发的,新创建的 ListItem 在 ItemUpdating/ItemUpdated 方法中不可用。

【问题讨论】:

    标签: c# sharepoint file-upload custom-fields event-receiver


    【解决方案1】:

    对于上传文件并将它们与列表项链接,您可以使用Sparqube Document 字段类型。 注意:这是商业插件。

    【讨论】:

      【解决方案2】:

      好的,因此创建一个自定义上传表单,它将文件上传输入控件和来自库的自定义字段与一个或多个 OOTB 或自定义 SPListFieldIterators 相结合并不是一件容易的事 - 这可能就是为什么微软决定将流程分成两个截然不同且完全不相关的操作。

      不过,允许此类功能具有内在价值,因为它使用户能够在单个原子操作中同时上传文件和持久化元数据,这样您的库中就不会存在一个位于没有任何识别信息的“ether”。

      那么它需要什么?几件事。

      首先是创建一个实用程序类,我称之为“FileUploader”,它就是这个样子。

      public class FileUploader
      {
          #region Fields
      
          private readonly SPList list;
          private readonly FileUpload fileUpload;
          private string contentTypeId;
          private string folder;
          private SPContext itemContext;
          private int itemId;
      
          #endregion
      
          #region Properties
      
          public bool IsUploaded
          {
              get
              {
                  return this.itemId > 0;
              }
          }
      
          public SPContext ItemContext
          {
              get
              {
                  return this.itemContext;
              }
          }
      
          public int ItemId
          {
              get
              {
                  return this.itemId;
              }
          }
      
          public string Folder
          {
              get
              {
                  return this.folder;
              }
      
              set
              {
                  this.folder = value;
              }
          }
      
          public string ContentTypeId
          {
              get
              {
                  return this.contentTypeId;
              }
      
              set
              {
                  this.contentTypeId = value;
              }
          }
      
          #endregion
      
          public FileUploader(SPList list, FileUpload fileUpload, string contentTypeId)
          {
              this.list = list;
              this.fileUpload = fileUpload;
              this.contentTypeId = contentTypeId;
          }
      
          public FileUploader(SPList list, FileUpload fileUpload, string contentTypeId, string folder)
          {
              this.list = list;
              this.fileUpload = fileUpload;
              this.contentTypeId = contentTypeId;
              this.folder = folder;
          }
      
          public event EventHandler FileUploading;
          public event EventHandler FileUploaded;
      
          public event EventHandler ItemSaving;
          public event EventHandler ItemSaved;
      
          public void ResetItemContext()
          {
              //This part here is VERY, VERY important!!!
              //This is where you "trick/hack" the SPContext by setting it's mode to "edit" instead
              //of "new" which gives you the ability to essentially initialize the
              //SPContext.Current.ListItem and set it's ItemId value. This of course could not have
              //been accomplished before because in "new" mode there is no ListItem. 
              //Once you've done all that then you can set the FileUpload.itemContext 
              //equal to the SPContext.Current.ItemContext. 
              if (this.IsUploaded)
              {
                  SPContext.Current.FormContext.SetFormMode(SPControlMode.Edit, true);
                  SPContext.Current.ResetItem();
                  SPContext.Current.ItemId = itemId;
      
                  this.itemContext = SPContext.Current;
              }
          }
      
          public bool TryRedirect()
          {
              try
              {
                  if (this.itemContext != null && this.itemContext.Item != null)
                  {
                      return SPUtility.Redirect(this.ItemContext.RootFolderUrl, SPRedirectFlags.UseSource, HttpContext.Current);
                  }
              }
              catch (Exception ex)
              {
                  // do something
                  throw ex;
              }
              finally
              {
              }
      
              return false;
      
          }
      
          public bool TrySaveItem(bool uploadMode, string comments)
          {
              bool saved = false;
              try
              {
                  if (this.IsUploaded)
                  {
                      //The SaveButton has a static method called "SaveItem()" which you can use
                      //to kick the whole save process into high gear. Just right-click the method
                      //in Visuak Studio and select "Go to Definition" in the context menu to see
                      //all of the juicy details.
                      saved = SaveButton.SaveItem(this.ItemContext, uploadMode, comments);
      
                      if (saved)
                      {
                          this.OnItemSaved();
                      }
                  }
              }
              catch (Exception ex)
              {
                  // do something
                  throw ex;
              }
              finally
              {
              }
      
              return saved;
          }
      
          public bool TrySaveFile()
          {
              if (this.fileUpload.HasFile)
              {
                  using (Stream uploadStream = this.fileUpload.FileContent)
                  {
                      this.OnFileUploading();
      
                      var originalFileName = this.fileUpload.FileName;
      
                      SPFile file = UploadFile(originalFileName, uploadStream);
      
                      var extension = Path.GetExtension(this.fileUpload.FileName);
      
                      this.itemId = file.Item.ID;
      
                      using (new EventFiringScope())
                      {
                          file.Item[SPBuiltInFieldId.ContentTypeId] = this.ContentTypeId;
                          file.Item.SystemUpdate(false);
      
                          //This code is used to guarantee that the file has a unique name.
                          var newFileName = String.Format("File{0}{1}", this.itemId, extension);
      
                          Folder = GetTargetFolder(file.Item);
      
                          if (!String.IsNullOrEmpty(Folder))
                          {
                              file.MoveTo(String.Format("{0}/{1}", Folder, newFileName));
                          }
      
                          file.Item.SystemUpdate(false);
                      }
      
                      this.ResetItemContext();
      
                      this.itemContext = SPContext.GetContext(HttpContext.Current, this.itemId, list.ID, list.ParentWeb);
                      this.OnFileUploaded();
      
                      return true;
                  }
              }
      
              return false;
          }
      
          public bool TryDeleteItem()
          {
              if (this.itemContext != null && this.itemContext.Item != null)
              {
                  this.ItemContext.Item.Delete();
      
                  return true;
              }
      
              return false;
          }
      
          private SPFile UploadFile(string fileName, Stream uploadStream)
          {
              SPList list = SPContext.Current.List;
      
              if (list == null)
              {
                  throw new InvalidOperationException("The list or root folder is not specified.");
              }
      
              SPWeb web = SPContext.Current.Web;
      
              SPFile file = list.RootFolder.Files.Add(fileName, uploadStream, true);
      
              return file;
          }
      
          private string GetTargetFolder(SPListItem item)
          {
              var web = item.Web;
              var rootFolder = item.ParentList.RootFolder.ServerRelativeUrl;
              var subFolder = GetSubFolderBasedOnContentType(item[SPBuiltInFieldId.ContentTypeId]);
      
              var folderPath = String.Format(@"{0}/{1}", rootFolder, subFolder);
              var fileFolder = web.GetFolder(folderPath);
      
              if (fileFolder.Exists) return folderPath;
      
              return Folder;
          }
      
          private void OnFileUploading()
          {
              EventHandler handler = this.FileUploading;
              if (handler != null)
              {
                  handler(this, EventArgs.Empty);
              }
          }
      
          private void OnFileUploaded()
          {
              EventHandler handler = this.FileUploaded;
              if (handler != null)
              {
                  handler(this, EventArgs.Empty);
              }
          }
      
          private void OnItemSaving()
          {
              EventHandler handler = this.ItemSaving;
              if (handler != null)
              {
                  handler(this, EventArgs.Empty);
              }
          }
      
          private void OnItemSaved()
          {
              EventHandler handler = this.ItemSaved;
              if (handler != null)
              {
                  handler(this, EventArgs.Empty);
              }
          }
      }
      

      然后我在我的“CustomUpload”类中使用它,这是我的 ASPX 页面的 CodeBehind。

      public partial class CustomUpload : LayoutsPageBase
      {
          #region Fields
      
          private FileUploader uploader;
      
          #endregion
      
          #region Properties
      
          public SPListItem CurrentItem { get; set; }
          public SPContentType ContentType { get; set; }
          public int DocumentID { get; set; }
      
          private SPList List;
      
          #endregion
      
          public CustomUpload()
          {
              SPContext.Current.FormContext.SetFormMode(SPControlMode.New, true);
          }
      
          protected override void OnInit(EventArgs e)
          {
              if (IsPostBack)
              {
                  // Get content type id from query string.
                  string contentTypeId = this.Request.QueryString["ContentTypeId"];
                  string folder = this.Request.QueryString["RootFolder"];
      
                  //ALL THE MAGIC HAPPENS HERE!!!
                  this.uploader = new FileUploader(SPContext.Current.List, this.NewFileUpload, contentTypeId, folder);
      
                  //These event handlers are CRITIAL! They are what enables you to perform the file
                  //upload, get the newly created ListItem, DocumentID and MOST IMPORTANTLY...
                  //the newly initialized ItemContext!!!
                  this.uploader.FileUploading += this.OnFileUploading;
                  this.uploader.FileUploaded += this.OnFileUploaded;
                  this.uploader.ItemSaving += this.OnItemSaving;
                  this.uploader.ItemSaved += this.OnItemSaved;
                  this.uploader.TrySaveFile();
              }
      
              base.OnInit(e);
          }
      
          protected void Page_Load(object sender, EventArgs e)
          {
              //put in whatever custom code you want...
          }
      
          protected void OnSaveClicked(object sender, EventArgs e)
          {
              this.Validate();
      
              var comments = Comments.Text;
      
              if (this.IsValid && this.uploader.TrySaveItem(true, comments))
              {
                  this.uploader.TryRedirect();
              }
              else
              {
                  this.uploader.TryDeleteItem();
              }
          }
      
          private void OnFileUploading(object sender, EventArgs e)
          {
          }
      
          private void OnFileUploaded(object sender, EventArgs e)
          {
              //This is the next VERY CRITICAL piece of code!!!
              //You need to retrieve a reference to the ItemContext that is created in the FileUploader
              //class and then set your SPListFieldIterator's ItemContext equal to it.
              this.MyListFieldIterator.ItemContext = this.uploader.ItemContext;
      
              ContentType = this.uploader.ItemContext.ListItem.ContentType;
      
              this.uploader.ItemContext.FormContext.SetFormMode(SPControlMode.Edit, true);
          }
      
          private void OnItemSaving(object sender, EventArgs e)
          {
          }
      
          private void OnItemSaved(object sender, EventArgs e)
          {
              using (new EventFiringScope())
              {
                  //This is where you could technically set any values for the ListItem that are
                  //not tied into any of your custom fields.
                  this.uploader.ItemContext.ListItem.SystemUpdate(false);
              }
          }
      }
      

      好的...那么所有这些代码的要点是什么?

      如果你不喜欢看我提供的 cmets,我会给你一个简短的总结。

      代码所做的基本上是使用 FileUploader 辅助类执行整个文件上传过程,并使用一系列附加到各种 SPItem 和 SPFile 相关事件的 EventHandlers (即保存/保存和上传/上传)允许新创建的 SPListItem 和 ItemContext 对象以及 SPListItem.Id 值与 CustomUpload 类中使用的 SPContext.Current.ItemContext 同步。一旦您拥有一个有效且新刷新的 ItemContext,您就可以“偷偷摸摸地”将您的 SPListFieldIterator(管理您的自定义字段)正在使用的现有 ItemContext 设置为等于在FileUpload 类并传回 CustomUpload 类,该类实际上具有对新创建的 ListItem 的引用!!!

      这里需要注意的另外一点是,您需要将 SPContext.Current.FormContextSPListFieldIterator 的控制模式从“新建”设置为“编辑” ”。如果您不这样做,那么您将无法设置 ItemContext 和 ListItem 属性,并且您的数据将不会被保存。您也不能通过将控制模式值设置为“编辑”来开始,因为这样 FormContext 和 SPListFieldIterator 将期望现有的 ItemContext 在初始页面或控件生命周期的任何时间点当然不会存在,因为您没有还没有上传文件!!!

      上述所有代码必须从 CustomUpload 类的 OnInit 方法执行。这样做的原因是您可以在初始化自身之前将新创建的 ItemContext 注入您的 SPListFieldIterator 并且它是子 SPField 控件(即您的自定义控件!!!)。一旦 SPListFieldIterator 具有对新创建的 ItemContext 的引用,它就可以使用所述 ItemContext 初始化它的所有子 SPField 控件,THAT 是您可以使用自定义上传页面的方式,该页面将 FileUpload 控件与自定义字段合并以及一个或多个 SPListFieldIterators 成功上传文件并将自定义字段中的所有值保存在单个原子操作中!

      完成并完成!

      注意:这个解决方案在“技术上”不是单一的或大气的操作,但它可以完成工作。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-07
        • 2010-09-08
        相关资源
        最近更新 更多