Introduction
This article describes uploading files in a sequence to a SharePoint Document Library and then using CAML for updating each file's custom meta data. This article also describes how to use the DWS Web Service to create folders/subfolders in a Doc Lib and how to use the List Web Service to upload/update files inside a Doc Lib.
Background
In my organization, a large number of documents are scanned each day (.pdf), and we maintain a complex electronic system of approving documents and filtering/searching based on the respective metadata. Files are named in a specific manner. I had to write a Web Service named File Shunter in VB.NET which contains two functions: a function that takes the input folder and uploads the files to a server into a specific folder (this function also creates folders/subfolders based on the rules in the file name using the SharePoint DWS Web service), and another function for each document being uploaded, that extracts all the required metadata from the file name, and once the file has been uploaded, uses CAML and the List Web service (http://sp portal/subsite/_vti_bin/lists.asmx) to update the required metadata columns for the file. I use Web.config to store/retrieve the doc library name, portal name, user authentication etc... This code uses web services, so it is not required to run the code on the server where the portal is running (in cases where the SharePoint Object Model is used).
Another feature is we have to maintain the version history of each file. Since a file can be uploaded multiple times, we need to keep revisions. For this purpose, if version history is enabled in the document library and if a file's metadata is changed after it has been uploaded, it will become a new version. While we need to access and update the current file without making another version while updating it for this purpose, the function bEnableVersion(True/False)
is used, which is set to True
when uploading the file so that if the file already exists, then a version is generated inside the Doc Lib. But when updating its metadata, version history is set to false (bEnableVersion(False)
). Another good feature of a document library is that it can be used as a mapped drive inside a file system (since it's a web folder). Our documentation dept. was used to accessing files from a shared folder before. Now, after files are uploaded to SharePoint, we just map the document library as a shared folder for each user so there is no difference for them to work/access these files. Just enable the WebClient service on the user's PC and map the Doc Lib (http://portal/site/doclib).
Using the code
There are two main functions: UploadDocument
and WSSUpdateFile
. The first function is passed the local file name (on the file system) and the remote file name (to be used inside the Doc Lib). Once the file is uploaded, the other function is passed the file name and any metadata column values to be updated. For test purposes, I created a column named "TestCol" in my test document library and passed it "Test Value". But this function can be passed as many number of metadata column values as required. Just add them in the XML batch inside the function with the column name and the value. To update file properties, normally, its ID is required to access it (even inside folders). I have used CAML to access the recently uploaded file and to get its ID, and then this ID is used in the List Web Service UpdateListItems
function to update the properties of the file.
Included is the function "CreateFolder
" which can be used to create any number of folder/subfolders within a Doc Lib. Most of the common required values like portal URL, Doc Lib name, user, password, domain, folder name (where files reside to be uploaded) are set into Web.config for easy modifications. The main function accesses these values to determine the required data and authentication. To upload the files, let's say from the D:\Test folder, in web.config, under the globalsharedpath
key, add "<add key="GlobalSharedPath" value="D:\"/>"
. Pass only "D:\" and once the program is running, pass only "Test" in the input folder name. One thing to remember is that some of the SharePoint Web Services are site specific, which means you need to reference the exact site/subsite to play with it. For example, if I have a subsite under http://portal/sitedirectory/site, then the List Web Service reference should be like http://portal/sitedirectory/site/_vti_bin/lists.asmx.
Web.config
<configuration>
<appSettings>
<add key="SharePointServer" value=http://SP Portal/Site/>
<add key="DocLibrary" value="Doclib"/>
<add key="User" value="User"/>
<add key="Domain" value="Domain"/>
<add key="Pwd" value="Pwd"/>
<add key="GlobalSharedPath" value="D:\"/>
</appSettings>
The code:
Public Function UploadDocument(ByVal localFile As String, _
ByVal remoteFile As String) As String
On Error GoTo handler
Dim r As Byte()
Dim Strm As System.IO.FileStream = New System.IO.FileStream(localFile, _
System.IO.FileMode.Open, System.IO.FileAccess.Read)
Dim reader As System.IO.BinaryReader = New System.IO.BinaryReader(Strm)
Dim filecontents As Byte() = reader.ReadBytes(CInt(Strm.Length))
reader.Close()
Strm.Close()
Dim sSPURL As String = ConfigurationManager.AppSettings("SharePointServer")
Dim sDocLib As String = ConfigurationManager.AppSettings("DocLibrary")
Dim sUser As String = ConfigurationManager.AppSettings("User")
Dim sPwd As String = ConfigurationManager.AppSettings("Pwd")
Dim sDomain As String = ConfigurationManager.AppSettings("Domain")
Dim sRemoteFileURL As String
Dim NC As System.Net.NetworkCredential = _
New System.Net.NetworkCredential(sUser, sPwd, sDomain)
sRemoteFileURL = sSPURL & "/" & sDocLib & _
"/" & Trim(LTrim(RTrim(remoteFile)))
sRemoteFileURL = Replace(sRemoteFileURL, " ", "%20")
sRemoteFileURL = Replace(sRemoteFileURL, "\", "/")
Dim m_WC As WebClient = New WebClient
m_WC.Credentials = NC
r = m_WC.UploadData(sRemoteFileURL, "PUT", filecontents)
Return "TRUE"
Exit Function
handler:
Return Err.Description
End Function
Public Function WSSUpdateFile(ByVal sFileName As String, ByVal sSiteDoc As String, _
ByVal sTestCol As String) As String
Dim sUser As String = ConfigurationManager.AppSettings("User")
Dim sPwd As String = ConfigurationManager.AppSettings("Pwd")
Dim sDomain As String = ConfigurationManager.AppSettings("Domain")
Dim sFileIDinList As String
Dim strBatch As String = ""
sSiteDoc = Replace(sSiteDoc, "%20", " ")
sSiteDoc = Replace(sSiteDoc, "\", "/")
Dim sFinalFilePath As String
Dim sSPURL As String = ConfigurationManager.AppSettings("SharePointServer")
Dim sDocLib As String = ConfigurationManager.AppSettings("DocLibrary")
Try
Dim netAccess As System.Net.NetworkCredential = _
New System.Net.NetworkCredential(sUser, sPwd, sDomain)
Dim listService As New SPLists.Lists
listService.Url = sSPURL & "/_vti_bin/lists.asmx"
listService.Credentials = netAccess
sFileIDinList = sGetID(listService.Url, sDocLib, sFileName)
If sFileIDinList <> "" Then
sFinalFilePath = sSPURL & "/" & sDocLib & "/" & sFileName
strBatch = "<Method ID='1' Cmd='Update'>" + _
"<Field Name = 'ID'>" & sFileIDinList & "</Field>" + _
"<Field Name = 'FileRef'>" & sFinalFilePath & "</Field>" + _
"<Field Name = 'TestCol'>" & sTestCol & "</Field>" + _
"</Method>"
Dim xmlDoc = New System.Xml.XmlDocument
Dim elBatch As System.Xml.XmlElement = xmlDoc.createelement("Batch")
elBatch.InnerXml = strBatch
Dim ndreturn As System.Xml.XmlNode = _
listService.UpdateListItems(sDocLib, elBatch)
End If
Return "TRUE"
Catch ex As Exception
Return ex.Message
End Try
End Function
Private Function sGetID(ByVal sURL As String, ByVal sListGUID As String, _
ByVal sFileName As String) As String
Dim sUser As String = ConfigurationManager.AppSettings("User")
Dim sPwd As String = ConfigurationManager.AppSettings("Pwd")
Dim sDomain As String = ConfigurationManager.AppSettings("Domain")
Dim netAccess As System.Net.NetworkCredential = _
New System.Net.NetworkCredential(sUser, sPwd, sDomain)
Dim L As New SPLists.Lists
L.Credentials = netAccess
L.Url = sURL
Dim xmldoc As XmlDocument = New XmlDocument
Dim query As XmlNode = xmldoc.CreateNode(XmlNodeType.Element, "Query", "")
query.InnerXml = "<OrderBy><FieldRef Name='Modified' " & _
"Ascending='False'></FieldRef></OrderBy>"""
Try
Dim caml As XmlNode = L.GetListItems(sListGUID, Nothing, query, _
Nothing, "1", Nothing)
Dim id As String = caml.ChildNodes(1).ChildNodes(1).Attributes("ows_ID").Value
Return id
Catch ex As Exception
Return ex.Message
End Try
End Function
Points of Interest
- Using Web Services instead of OM
- Creating folders inside Doc Lib
- Enabling/disabling version history