How to automatically load files into SharePoint - c#

We are having someone manually load weekly generated excel spreadsheets into SharePoint. I'm sure there is a way to automate this. I don't know a lot about SharePoint, and maybe it's really as simple as just knowing the folder SharePoint is moving the files to and copying them directly there. Or maybe it requires some programming to get it automatically load new files put in a given directory into SharePoint.
Either way, I would just like someone to point me in the right direction here.

You will need to upload the file using the copy web service in SharePoint. I am not sure what version of SharePoint you are running but I am assuming 2007. Here is a sample project.
public void UploadFile(string destinationFolderPath,
byte[] fileBytes,
string fileName,
bool overwrite,
string sourceFileUrl,
string lastVersionUrl)
{
List<Sharepoint.FieldInformation> fields = new List<Sharepoint.FieldInformation>();
Sharepoint.FieldInformation fieldInfo;
fieldInfo = new Sharepoint.FieldInformation();
fieldInfo.Id = Microsoft.SharePoint.SPBuiltInFieldId.Title;
fieldInfo.Value = "New title";
fieldInfo.DisplayName = "Title";
fieldInfo.Type = YetAnotherMigrationTool.Library.SP2007.Sharepoint.FieldType.Text;
fieldInfo.InternalName = "Title";
fields.Add(fieldInfo);
string[] url;
if (string.IsNullOrEmpty(destinationFolderPath))
url = new string[] { string.Format("{0}/{1}/{2}", _siteUrl, _name, fileName) };
else
url = new string[] { string.Format("{0}/{1}/{2}{3}", _siteUrl, _name, destinationFolderPath, fileName) };
Sharepoint.CopyResult[] result;
Sharepoint.Copy service = new Sharepoint.Copy();
service.Url = _siteUrl + "/_vti_bin/Copy.asmx";
service.Credentials = new NetworkCredential(Settings.Instance.User, Settings.Instance.Password);
service.Timeout = 600000;
uint documentId = service.CopyIntoItems(sourceFileUrl, url, fields.ToArray(), fileBytes, out result);
}
public void SetContentType(List<string> ids, string contentType)
{
ListsService.Lists service = new YetAnotherMigrationTool.Library.SP2007.ListsService.Lists();
service.Url = _siteUrl + "/_vti_bin/Lists.asmx";
service.Credentials = new NetworkCredential(Settings.Instance.User, Settings.Instance.Password);
string strBatch = "";
for (int i = 1; i <= ids.Count; i++)
{
strBatch += #"<Method ID='"+i.ToString()+#"' Cmd='Update'><Field Name='ID'>" + ids[i-1] + "</Field><Field Name='ContentType'>"+contentType+"</Field></Method>";
}
XmlDocument xmlDoc = new XmlDocument();
XmlElement elBatch = xmlDoc.CreateElement("Batch");
elBatch.SetAttribute("OnError", "Continue");
elBatch.SetAttribute("ListVersion", "10");
elBatch.SetAttribute("ViewName", "");
elBatch.InnerXml = strBatch;
result = service.UpdateListItems(_name, elBatch);
}

You could write a PowerShell script that copies the document into the document library via WebDav:
Assuming you have your document library at http://server/SomeWeb/DocumentLibrary/Folder:
copy-item somesheet.xlsx \\server\SomeWeb\DocumentLibrary\Folder

Related

How to extract all pages and attachments from PDF to PNG

I am trying to create a process in .NET to convert a PDF and all it's pages + attachments to PNGs. I am evaluating libraries and came across PDFiumSharp but it is not working for me. Here is my code:
string Inputfile = "input.pdf";
string OutputFolder = "Output";
string fileName = Path.GetFileNameWithoutExtension(Inputfile);
using (PdfDocument doc = new PdfDocument(Inputfile))
{
for (int i = 0; i < doc.Pages.Count; i++)
{
var page = doc.Pages[i];
using (var bitmap = new PDFiumBitmap((int)page.Width, (int)page.Height, false))
{
page.Render(bitmap);
var targetFile = Path.Combine(OutputFolder, fileName + "_" + i + ".png");
bitmap.Save(targetFile);
}
}
}
When I run this code, I get this exception:
screenshot of exception
Does anyone know how to fix this? Also does PDFiumSharp support extracting PDF attachments? If not, does anyone have any other ideas on how to achieve my goal?
PDFium does not look like it supports extracting PDF attachments. If you want to achieve your goal, then you can take a look at another library that supports both extracting PDF attachments as well as converting PDFs to PNGs.
I am an employee of the LEADTOOLS PDF SDK which you can try out via these 2 nuget packages:
https://www.nuget.org/packages/Leadtools.Pdf/
https://www.nuget.org/packages/Leadtools.Document.Sdk/
Here is some code that will convert a PDF + all attachments in the PDF to separate PNGs in an output directory:
SetLicense();
cache = new FileCache { CacheDirectory = "cache" };
List<LEADDocument> documents = new List<LEADDocument>();
if (!Directory.Exists(OutputDir))
Directory.CreateDirectory(OutputDir);
using var document = DocumentFactory.LoadFromFile("attachments.pdf", new LoadDocumentOptions { Cache = cache, LoadAttachmentsMode = DocumentLoadAttachmentsMode.AsAttachments });
if (document.Pages.Count > 0)
documents.Add(document);
foreach (var attachment in document.Attachments)
documents.Add(document.LoadDocumentAttachment(new LoadAttachmentOptions { AttachmentNumber = attachment.AttachmentNumber }));
ConvertDocuments(documents, RasterImageFormat.Png);
And the ConvertDocuments method:
static void ConvertDocuments(IEnumerable<LEADDocument> documents, RasterImageFormat imageFormat)
{
using var converter = new DocumentConverter();
using var ocrEngine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD);
ocrEngine.Startup(null, null, null, null);
converter.SetOcrEngineInstance(ocrEngine, false);
converter.SetDocumentWriterInstance(new DocumentWriter());
foreach (var document in documents)
{
var name = string.IsNullOrEmpty(document.Name) ? "Attachment" : document.Name;
string outputFile = Path.Combine(OutputDir, $"{name}.{RasterCodecs.GetExtension(imageFormat)}");
int count = 1;
while (File.Exists(outputFile))
outputFile = Path.Combine(OutputDir, $"{name}({count++}).{RasterCodecs.GetExtension(imageFormat)}");
var jobData = new DocumentConverterJobData
{
Document = document,
Cache = cache,
DocumentFormat = DocumentFormat.User,
RasterImageFormat = imageFormat,
RasterImageBitsPerPixel = 0,
OutputDocumentFileName = outputFile,
};
var job = converter.Jobs.CreateJob(jobData);
converter.Jobs.RunJob(job);
}
}

Rally C#: How to upload a collection of attachments and associate with a user story?

I have refereed to the other examples on this website, but found a major difference in my method. (Please be patient)
I am trying to iterate over a directory of files and upload each file as an attachment and associate to a user story.
I am only able to attach 1 file for a user story as of now.
I see that every attachment has to be encoded to a base 64 string and it must have a the size in bytes.
Here is my code so far:
public void createUsWithAttachmentList(string workspace, string project, string userStoryName, string userStoryDescription)
{
//authentication
this.EnsureRallyIsAuthenticated();
//DynamicJSONObject for AttachmentContent
DynamicJsonObject myAttachmentContent = new DynamicJsonObject();
//Length calculated from Base64String converted back
int imageNumberBytes = 0;
//Userstory setup
DynamicJsonObject toCreate = new DynamicJsonObject();
toCreate["Workspace"] = workspace;
toCreate["Project"] = project;
toCreate["Name"] = userStoryName;
toCreate["Description"] = userStoryDescription;
//Trying to get a list of all the file paths within a given directory, this directory would contain .png files that need to be associated to a user story.
string[] attachmentPath = Directory.GetFiles("C:\\Users\\user\\Desktop\\RallyAttachments");
This foreach loop is confusing. I am trying to iterate over each file in the directory in order to convert it into a base64 string, and at the same time acquire the number of bytes for each file as an int.
foreach (string fileName in attachmentPath)
{
Image myImage = Image.FromFile(fileName);
string imageBase64String = imageToBase64(myImage, System.Drawing.Imaging.ImageFormat.Png);
imageNumberBytes = Convert.FromBase64String(imageBase64String).Length;
//I am stuck here to be exact because there are multiple imageBase64Strings due to the collection of files located inside the directory. AND the below line is wrong because I have a list of imageBase64Strings that were generated from iterating through the string[] attachmentPath.
myAttachmentContent[RallyField.content] = imageBase64String;
}
try
{
//create user story
CreateResult createUserStory = _api.Create(RallyField.attachmentContent, myAttachmentContent);
//create attachment
CreateResult myAttachmentContentCreateResult = _api.Create(RallyField.attachmentContent, myAttachmentContent);
String myAttachmentContentRef = myAttachmentContentCreateResult.Reference;
//DynamicJSONObject for Attachment Container
//I assume I would need a separate container for each file in my directory containing the attachments.
DynamicJsonObject myAttachment = new DynamicJsonObject();
myAttachment["Artifact"] = createUserStory.Reference;
myAttachment["Content"] = myAttachmentContentRef;
myAttachment["Name"] = "AttachmentFromREST.png";
myAttachment["Description"] = "Email Attachment";
myAttachment["ContentType"] = "image/png";
myAttachment["Size"] = imageNumberBytes;
//create & associate the attachment
CreateResult myAttachmentCreateResult = _api.Create(RallyField.attachment, myAttachment);
Console.WriteLine("Created User Story: " + createUserStory.Reference);
}
catch (WebException e)
{
Console.WriteLine(e.Message);
}
}
Note: I am planning on extending this method to support multiple file types, and I thing I would need to get the file type of each file in the directory and proceed accordingly.
Any ideas on how to go about writing this?
You've got all the parts implemented- we just need to move it around a little bit. Create the story once at the beginning, and then each time through the loop make a new AttachmentContent and a new Attachment for each file.
public void createUsWithAttachmentList(string workspace, string project, string userStoryName, string userStoryDescription)
{
//authentication
this.EnsureRallyIsAuthenticated();
//Userstory setup
DynamicJsonObject toCreate = new DynamicJsonObject();
toCreate["Workspace"] = workspace;
toCreate["Project"] = project;
toCreate["Name"] = userStoryName;
toCreate["Description"] = userStoryDescription;
//Create the story first
try
{
//create user story
CreateResult createUserStory = _api.Create(RallyField.userStory, toCreate);
//now loop over each file
string[] attachmentPath = Directory.GetFiles("C:\\Users\\user\\Desktop\\RallyAttachments");
foreach (string fileName in attachmentPath)
{
//DynamicJSONObject for AttachmentContent
DynamicJsonObject myAttachmentContent = new DynamicJsonObject();
Image myImage = Image.FromFile(fileName);
string imageBase64String = imageToBase64(myImage, System.Drawing.Imaging.ImageFormat.Png);
int imageNumberBytes = Convert.FromBase64String(imageBase64String).Length;
myAttachmentContent[RallyField.content] = imageBase64String;
//create the AttachmentConent
CreateResult myAttachmentContentCreateResult = _api.Create(RallyField.attachmentContent, myAttachmentContent);
String myAttachmentContentRef = myAttachmentContentCreateResult.Reference;
//create an Attachment to associate to story
DynamicJsonObject myAttachment = new DynamicJsonObject();
myAttachment["Artifact"] = createUserStory.Reference;
myAttachment["Content"] = myAttachmentContentRef;
myAttachment["Name"] = "AttachmentFromREST.png";
myAttachment["Description"] = "Email Attachment";
myAttachment["ContentType"] = "image/png";
myAttachment["Size"] = imageNumberBytes;
//create & associate the attachment
CreateResult myAttachmentCreateResult = _api.Create(RallyField.attachment, myAttachment);
}
}
catch (WebException e)
{
Console.WriteLine(e.Message);
}
}

Problems with PDB file through CodeDom

I have been creating a plugin model where code will be written on the server and a dll output will be created and downloaded to the client side.
This works well, but as we also want to have debugging support for the code that is generated in server, I tried the following code to download on client.
public OutputResponse GetObject(string fileName, string sourceCode)
{
string[] assemblies = this.GetAssemblies();
string codeLanguage = "cs";
var dllFilePath = Path.Combine(Path.GetTempPath(), fileName + ".dll");
var pdbFilePath = Path.Combine(Path.GetTempPath(), fileName + ".pdb");
//Delete Existing file
if (File.Exists(dllFilePath))
File.Delete(dllFilePath);
if (File.Exists(pdbFilePath))
File.Delete(pdbFilePath);
Dictionary<string, string> compilerInfo = new Dictionary<string, string>();
compilerInfo.Add("CompilerVersion", "v4.0");
CodeDomProvider provider = CodeDomProvider.CreateProvider(codeLanguage, compilerInfo);
var compilerParameter = new CompilerParameters();
compilerParameter.WarningLevel = 3;
compilerParameter.GenerateExecutable = false;
compilerParameter.GenerateInMemory = false;
compilerParameter.IncludeDebugInformation = true;
compilerParameter.CompilerOptions = "/debug:pdbonly";
compilerParameter.TempFiles = new TempFileCollection(Environment.GetEnvironmentVariable("TEMP"), true);
compilerParameter.TempFiles.KeepFiles = true;
compilerParameter.OutputAssembly = dllFilePath;
foreach (var assembly in assemblies)
compilerParameter.ReferencedAssemblies.Add(assembly);
var results = provider.CompileAssemblyFromSource(compilerParameter, sourceCode);
string sourceFile = string.Empty;
string pdbFile = Path.Combine(Path.GetDirectoryName(results.PathToAssembly), string.Concat(Path.GetFileNameWithoutExtension(results.PathToAssembly), ".pdb"));
foreach(string file in results.TempFiles)
{
var extension = Path.GetExtension(file);
switch(extension)
{
case ".cs":
sourceFile = file;
break;
}
}
var response = new OutputResponse(results.PathToAssembly, pdbFile, sourceFile);
return response;
}
The dll is generated correctly, and I am renaming the pdb and source file to the dll name, and downloaded to the client folder.
Now when a method is called using in the application which plugs in the dll, the Visual Studio cannot attach the debugger. It says "A matching symbol file was not found in this folder".
Can anyone help me on how to solve this problem.

How to copy file from one library to another library using CSOM?

I need to copy a particular file from one library to another library.
At first, need to check if file is existing in that library.
If Existing, then need to overwrite file content and new sharepoint version should be updated for that document.
I need to do this using c# CSOM and sharepoint version is 2013.
Thanks in advance :)
public static void CopyDocuments(string srcUrl, string destUrl, string srcLibrary, string destLibrary, Login _login)
{
// set up the src client
SP.ClientContext srcContext = new SP.ClientContext(srcUrl);
srcContext.AuthenticationMode = SP.ClientAuthenticationMode.FormsAuthentication;
srcContext.FormsAuthenticationLoginInfo = new SP.FormsAuthenticationLoginInfo(_login.UserName, _login.Password);
// set up the destination context (in your case there is no needs to create a new context, because it would be the same library!!!!)
SP.ClientContext destContext = new SP.ClientContext(destUrl);
destContext.AuthenticationMode = SP.ClientAuthenticationMode.FormsAuthentication;
destContext.FormsAuthenticationLoginInfo = new SP.FormsAuthenticationLoginInfo(_login.UserName, _login.Password);
// get the list and items
SP.Web srcWeb = srcContext.Web;
SP.List srcList = srcWeb.Lists.GetByTitle(srcLibrary);
SP.ListItemCollection col = srcList.GetItems(new SP.CamlQuery());
srcContext.Load(col);
srcContext.ExecuteQuery();
// get the new list
SP.Web destWeb = destContext.Web;
destContext.Load(destWeb);
destContext.ExecuteQuery();
foreach (var doc in col)
{
try
{
if (doc.FileSystemObjectType == SP.FileSystemObjectType.File)
{
// get the file
SP.File f = doc.File;
srcContext.Load(f);
srcContext.ExecuteQuery();
// build new location url
string nLocation = destWeb.ServerRelativeUrl.TrimEnd('/') + "/" + destLibrary.Replace(" ", "") + "/" + f.Name;
// read the file, copy the content to new file at new location
SP.FileInformation fileInfo = SP.File.OpenBinaryDirect(srcContext, f.ServerRelativeUrl);
SP.File.SaveBinaryDirect(destContext, nLocation, fileInfo.Stream, true);
}
if (doc.FileSystemObjectType == SP.FileSystemObjectType.Folder)
{
// load the folder
srcContext.Load(doc);
srcContext.ExecuteQuery();
// get the folder data, get the file collection in the folder
SP.Folder folder = srcWeb.GetFolderByServerRelativeUrl(doc.FieldValues["FileRef"].ToString());
SP.FileCollection fileCol = folder.Files;
// load everyting so we can access it
srcContext.Load(folder);
srcContext.Load(fileCol);
srcContext.ExecuteQuery();
foreach (SP.File f in fileCol)
{
// load the file
srcContext.Load(f);
srcContext.ExecuteQuery();
string[] parts = null;
string id = null;
if (srcLibrary == "My Files")
{
// these are doc sets
parts = f.ServerRelativeUrl.Split('/');
id = parts[parts.Length - 2];
}
else
{
id = folder.Name;
}
// build new location url
string nLocation = destWeb.ServerRelativeUrl.TrimEnd('/') + "/" + destLibrary.Replace(" ", "") + "/" + id + "/" + f.Name;
// read the file, copy the content to new file at new location
SP.FileInformation fileInfo = SP.File.OpenBinaryDirect(srcContext, f.ServerRelativeUrl);
SP.File.SaveBinaryDirect(destContext, nLocation, fileInfo.Stream, true);
}
}
}
catch (Exception ex)
{
Log("File Error = " + ex.ToString());
}
}
}
Source: https://sharepoint.stackexchange.com/questions/114033/how-do-i-move-files-from-one-document-library-to-another-using-jsom
I strongly advise against using the approach suggested by Nikerym. You don't want to download the bytes only to upload them unmodified. It's slow and error-prone. Instead, use the built-in method provided by the CSOM API.
https://learn.microsoft.com/en-us/previous-versions/office/sharepoint-server/mt162553(v=office.15)?redirectedfrom=MSDN
var srcPath = "https://YOUR.sharepoint.com/sites/xxx/SitePages/Page.aspx";
var destPath = $"https://YOUR.sharepoint.com/sites/xxx/SitePages/CopiedPage.aspx";
MoveCopyUtil.CopyFileByPath(ctx, ResourcePath.FromDecodedUrl(srcPath), ResourcePath.FromDecodedUrl(destPath), false, new MoveCopyOptions());
ctx.ExecuteQuery();
You can configure the override behavior by adjusting the 4th and 5th arguments of the function signature.
[...]
bool overwrite,
MoveCopyOptions options
https://learn.microsoft.com/en-us/previous-versions/office/sharepoint-server/mt844930(v=office.15)

updated listitem attributes aren't commiting changes to sharepoint

i'm uploading a document to sharepoint.. however i would like to provide a custom name rather than it inherit the name of the file which im uploading.
my code was based on this solution: http://www.codeproject.com/Articles/103503/How-to-upload-download-a-document-in-SharePoint-20.aspx
however this doesnt work.
Additionally, i would also like to provide a title of the file:
so i wanted to update the title:
uploadFile.ListItemAllFields.FieldValues["Title"] = "my custom title";
However, once the file has completed its upload..i login to sharepoint and notice the title hasnt been applied.
how can i intergrate uploading the file and applying a new name?
many thanks,
EDIT:
using (var clientContext = GetNewContext())
{
var uploadLocation = string.Format("{0}{1}/{2}", SiteUrl, Helpers.ListNames.RequestedDocuments, Path.GetFileName(document));
//Get Document List
var documentslist = clientContext.Web.Lists.GetByTitle(Helpers.ListNames.RequestedDocuments);
var fileCreationInformation = new FileCreationInformation
{
Content = System.IO.File.ReadAllBytes(document), //Assign to content byte[] i.e. documentStream
Overwrite = true, //Allow owerwrite of document
Url = uploadLocation //Upload URL,
};
var uploadFile = documentslist.RootFolder.Files.Add(fileCreationInformation);
uploadFile.ListItemAllFields.FieldValues["Title"] = title;
uploadFile.ListItemAllFields.Update();
clientContext.ExecuteQuery();
}
site.SubmitChanges(ConflictMode.FailOnFirstConflict, true);
You are missing a call to clientContext.Load after you add the file to the Files collection. See these blog posts for more information:
https://www.c-sharpcorner.com/code/965/programmatically-upload-document-using-client-object-model-in-sharepoint.aspx
https://zimmergren.net/sp-2010-uploading-files-using-the-client-om-in-sharepoint-2010/
This code sample is from the first blog post linked above:
public Boolean UploadDocument(String fileName, String filePath, List metaDataList)
{
SP.ClientContext ctx = new SP.ClientContext("http: //yoursharepointURL");
Web web = ctx.Web;
FileCreationInformation newFile = new FileCreationInformation();
newFile.Content = System.IO.File.ReadAllBytes(#"C: \TestFile.doc");
newFile.Url = " / " + fileName;
List docs = web.Lists.GetByTitle("Shared Documents");
Microsoft.SharePoint.Client.File uploadFile = docs.RootFolder.Files.Add(newFile);
context.Load(uploadFile);
context.ExecuteQuery();
SPClient.ListItem item = uploadFile.ListItemAllFields;
//Set the metadata
string docTitle = string.Empty;
item["Title"] = docTitle;
item.Update();
context.ExecuteQuery();
}
Are you calling Update after setting the field values?
uploadFile.ListItemAllFields.Update();
instead of setting:
uploadFile.ListItemAllFields.FieldValues["Title"] = title;
uploadFile.ListItemAllFields.Update();
set it as follows:
uploadFile.ListItemAllFields["Title"] = title;
uploadFile.ListItemAllFields.Update();

Categories