I'm displaying files from a Sharepoint 2010 document library on my web page. I'm successfully showing a list of the files and their icons. However, some of the files are in subfolders, and instead of showing the file icon, I'd like to show a folder icon.
How do I detect when a file is in a subfolder?
I could parse each file's ServerRelativeUrl to determine the folder structure. I'm hoping there is another way.
Here is the Sharepoint library:
And here is the code which produces the list of files in that library:
using (ClientContext clientContext = new ClientContext(SharepointSite))
{
var query = new CamlQuery
{
ViewXml = "<View Scope='RecursiveAll'>" +
"<Query>" +
"<Where>" +
"<Eq>" +
"<FieldRef Name='FSObjType' />" +
"<Value Type='Integer'>0</Value>" +
"</Eq>" +
"</Where>" +
"</Query>" +
"</View>"
};
var sourceList = clientContext.Web.Lists.GetByTitle("Test Library");
var files = sourceList.GetItems(query);
clientContext.Load(files);
clientContext.ExecuteQuery();
foreach (var file in files)
{
var id = file.Id;
var filename = file["FileLeafRef"].ToString();
var iconName = clientContext.Web.MapToIcon(filename, string.Empty, IconSize.Size16);
clientContext.ExecuteQuery();
var imgUrl = "http://sharepointsite/_layouts/images/" + iconName.Value;
Image iconImage = new Image {ImageUrl = imgUrl};
clientContext.Load(file.ParentList);
clientContext.ExecuteQuery();
var listUrl = file.ParentList.DefaultDisplayFormUrl;
HyperLink docLink = new HyperLink
{
Text = filename,
NavigateUrl = listUrl + "?ID=" + id //ToDo: fix
};
HtmlTableRow row = new HtmlTableRow();
HtmlTableCell cell1 = new HtmlTableCell();
cell1.Controls.Add(iconImage);
HtmlTableCell cell2 = new HtmlTableCell();
cell2.Controls.Add(docLink);
row.Cells.Add(cell1);
row.Cells.Add(cell2);
tbFiles.Rows.Add(row);
}
}
which results in:
Only the first file in the list is actually in the top-most library. The rest are in "Test Folder".
It might helpful to understand the available options for the view scope property of your CAML query:
Default: Gets files and subfolders from a specific folder
RecursiveAll: Gets files and subfolders from all folders
FilesOnly: Gets only files (no folders) from a specific folder
Recursive: Gets only files (no folders) from all folders
If you want to maintain a hierarchical folder structure, you have two options:
Get all files in the library (using a view scope of Recursive or RecursiveAll) and try to reconstruct the file structure yourself by post-processing the results
Get only files and folders from one folder at a time (using a view scope of Default), executing a new query whenever you want to drill down into the contents of a subfolder
Both approaches are equally valid, but in general I'd recommend the second. They both have their downsides: the first requires a larger up-front network request and more post-processing logic to assemble the hierarchy, while the second requires multiple network requests to retrieve all the data.
If taking the second approach, you can limit your CAML query to a specific folder by setting the CamlQuery's FolderServerRelativeUrl property to the URL of the desired folder. When working with the items retrieved from a specific folder, you can check their FileSystemObjectType property to determine if they are files or folders; if they are folders you can access their Folder property to get the associated folder object, from which you can get the ServerRelativeUrl property to use in your next query to get items from that folder.
Related
I am having folder structure like
(Container)->(1)School->(2)Staffs
->(2.a)OfficeStaffs
-> (2.a.i)Admin ->(Blobs)
-> (2.a.ii)Clerk->(Blobs)
->(2.b)Teachers
->(2.b.i)SeniorStudents ->(Year)->AttendanceReport.xlx
->ExamReport.xlx
->(2.b.ii)JuniorStudents ->(Year)->AttendanceReport.xlx
->ExamReport.xlx
School is my parent folder and all other folders are sub folders. Now I need to find blobs using folder name. The folder name may persist in middle. For example user have the search options in the UI by Staff type or Teachers or Students Type or By Year. There is no mandatory options to search blobs by folder level one by one. If the User selects Teachers , need to display all teachers and Students folder with respective blobs. If the user selects year , we need to get all the blobs belongs to the particular year folder. In this case, we will receive 'Year' value from user. We will not be knowing its parent folders. Based on the year only we need to retrieve. If User selects OfficeStaffs and Teachers, we need to retrieve all the subfolders and blobs from both the folders.
I tried with Blob Prefix to get middle folder but no luck. It is always expecting the Initial folder path and with next folders in order basis. Could not able to get the middle folder.
BlobContainerClient client = new BlobContainerClient(connectionString, containerName);
List<FileData> files = new List<FileData>();
await foreach (BlobItem file in client.GetBlobsAsync(prefix: "SeniorStudents"))
{
files.Add(new FileData
{
FileName = file.Name
}
}
This is not getting the blobs under SeniorStudents folder. It is returning empty. Please help me on this. Thanks.
I am having folder structure like
No, you don't. Unless hierarchical namespace is enabled all the (sub)folders you see are virtual. Those folders are defined by the name of the blob. Each / will be seen as a virtual folder in the storage explorer.
In your case you have multiple blobs in a container:
School/Staffs/OfficeStaffs/Admin/Blob1.ext
School/Staffs/OfficeStaffs/Clerk/Blob2.ext
School/Staffs/Teachers/SeniorStudents/2022/AttendanceReport.xlx
School/Staffs/Teachers/SeniorStudents/2022/ExamReports.xlx
School/Staffs/Teachers/JuniorStudents/2022/AttendanceReport.xlx
School/Staffs/Teachers/JuniorStudents/2022/ExamReports.xlx
As you can see it is a flat list. When you try to find blobs based on a prefix you need to remember it is like the equivalent of matching a string using the C# string.StartsWith method.
So with prefix School/Staffs/OfficeStaffs/Admin/ you will find blob 1, a prefix School/Staffs/Teachers will give you blobs 3 to 6. The prefix Staffs does not list any blobs as there are no blobs that have the text staffs at the start of their name.
In your case, that means that you will have to get all blobs, split their names using for example string.Split(). For example, the code below finds all blobs that are somehow in a folder named SeniorStudents, no matter at what level that virtual folder is present:
class FileData
{
public string FileName { get;set;}
public IEnumerable<string> Folders => FileName.Split('/').SkipLast(1);
}
...
await foreach (BlobItem file in client.GetBlobsAsync())
{
files.Add(new FileData
{
FileName = file.Name
});
}
var targetFiles = files.Where(f => f.Folders.Contains(("SeniorStudents")));
In the above example, if you want all 2022 files for all teachers you can do:
var targetFiles = files.Where(f => f.Folders.Contains("Teachers") && f.Folders.Contains("2022"));
Alternative
If you have lots of blobs the above method will force you to perform an inefficient query to get maybe 5 results out of 2000 blobs because you need to get all the blobs before you can determine whether they match the criteria.
As an alternative you might want to add tags to your blobs, each tag representing a folder or category. It is then easy to find all blobs having a specific tag. Beware the limits, there may be up to 10 tags defined on a given blob, see the docs.
Fetching the folders from container
string connectionString = "Connection String";
List<string> dir = new List<string>();
BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);
string containerName = "containerName";
BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(containerName);
var blobitem = containerClient.GetBlobs(prefix: "test");
List<FileData> files = new List<FileData>();
foreach (var file in blobitem)
{
Console.WriteLine(file.Name);
string[] sub_names = file.Name.Split('/');
Console.WriteLine(sub_names.Length);
files.Add(new FileData
{
FileName = file.Name
});
if (sub_names.Length > 1 && !dir.Contains(sub_names[sub_names.Length - 1]))
{
dir.Add(sub_names[sub_names.Length - 1]);
}
}
foreach (var item in dir)
{
Console.WriteLine(item);
}
Fetching the files and folder structure using code
Sample Output using the C# code
Blob storage in explorer
In Azure Portal
Update
For fetching blobs or files from azure portal and also uploading files to specific folder can also be done by using the below code.
var blob_Container = GetBlobContainer(containerName);
var blob_Directory = blob_Container.GetDirectoryReference(directoryName);
var blob_Infos = new List<BlobFileInfo>();
var blobs = blob_Directory.ListBlobs().ToList();
foreach (var blob in blobs)
{
if (blob is CloudBlockBlob)
{
var blob_FileName = blob.Uri.Segments.Last().Replace("%20", " ");
var blob_FilePath = blob.Uri.AbsolutePath.Replace(blob.Container.Uri.AbsolutePath + "/", "").Replace("%20", " ");
var blob_Path = blob_FilePath.Replace("/" + blob_FileName, "");
blob_Infos.Add(new BlobFileInfo
{
File = blob_FileName,
Path = blob_Path,
Blob_FilePath = blob_FilePath,
Blob = blob
});
}
if (blob is CloudBlobDirectory)
{
var blob_Dir = blob.Uri.OriginalString.Replace(blob.Container.Uri.OriginalString + "/", "");
blob_Dir = blob_Dir.Remove(blob_Dir.Length - 1);
var subBlobs = ListFolderBlobs(containerName, blob_Dir);
blob_Infos.AddRange(subBlobs);
}
}
return blob_Infos;
I am getting a PropertyException on the 2nd part of this code. The 1st part uploads the file as expected. After the context.ExecuteQuery(); I am then getting:
'uploadedFile.CheckInComment' threw an exception of type
'Microsoft.SharePoint.Client.PropertyOrFieldNotInitializedException'
I am not sure why as the context should be OKsince it did upload the file.
I am going to try to update some meta data fields on the document I just uploaded.
Folder currentRunFolder = site.GetFolderByServerRelativeUrl(barRootFolderRelativeUrl + "/");
FileCreationInformation newFile = new FileCreationInformation
{
Content = System.IO.File.ReadAllBytes(#p),
Url = Path.GetFileName(#p),
Overwrite = true
};
currentRunFolder.Files.Add(newFile);
currentRunFolder.Update();
context.ExecuteQuery();
newUrl = siteUrl + barRootFolderRelativeUrl + "/" + Path.GetFileName(#p);
// Set document properties
Microsoft.SharePoint.Client.File uploadedFile = context.Web.GetFileByServerRelativeUrl(newUrl);
ListItem listItem = uploadedFile.ListItemAllFields;
listItem["TestEQCode"] = "387074";
listItem.Update();
context.ExecuteQuery();
Could you try this.
currentRunFolder.Files.Add(newFile);
//currentRunFolder.Update();
context.Load(newFile);
context.ExecuteQuery();
//newUrl = siteUrl + barRootFolderRelativeUrl + "/" + Path.GetFileName(#p);
// Set document properties
//Microsoft.SharePoint.Client.File uploadedFile = context.Web.GetFileByServerRelativeUrl(newUrl);
ListItem listItem = newFile.ListItemAllFields;
listItem["TestEQCode"] = "387074";
listItem.Update();
context.ExecuteQuery();
Ok so eventhough the ListItems is NULL I can set the TestEQCode and update and the field is getting updated on the SharePoint side. This whole time I was concerned on the ListItems getting the actual meta data list, but really it doesn't need that. I just will have to Hard Code those items and it will update. –
I want to upload an image to a specific folder and if that folder does not exist create it and make this folder shareable to another email address.
I use the below code:
MetadataChangeSet changeSetfile = new MetadataChangeSet.Builder()
.SetTitle("Test.jpg")
.SetMimeType("image/jpeg")
.Build();
MetadataChangeSet changeSetfolder = new MetadataChangeSet.Builder()
.SetTitle("New folder")
.SetMimeType(DriveFolder.MimeType)
.SetStarred(true)
.Build();
DriveClass.DriveApi
.GetRootFolder(_googleApiClient)
.CreateFolder(_googleApiClient, changeSetfile) ;
DriveClass.DriveApi
.GetRootFolder(_googleApiClient)
.CreateFile(_googleApiClient, changeSetfolder, contentResults.DriveContents);
First, you have to check if the folder exist. According to this tutorial - Search files on Google Drive with C#, you can use:
By file name
What if you know the name of the file or part of the name well you can search on name. Fulltext is also nice it allows you to search on “Full text of the file including name, description, content, and indexable text.”
List files only directories
It is also possible to search for only one type of file say you just want to return all the Google sheets on your account? What if you just wanted to find all the folders well folders is a mimetype of “application/vnd.google-apps.folder” so we can search on just that.
Upon check if it is existing, get the ID to be place in the parents parameter:
var folderId = "0BwwA4oUTeiV1TGRPeTVjaWRDY1E";
var fileMetadata = new File()
{
Name = "photo.jpg",
Parents = new List<string>
{
folderId
}
};
FilesResource.CreateMediaUpload request;
using (var stream = new System.IO.FileStream("files/photo.jpg",
System.IO.FileMode.Open))
{
request = driveService.Files.Create(
fileMetadata, stream, "image/jpeg");
request.Fields = "id";
request.Upload();
}
var file = request.ResponseBody;
Console.WriteLine("File ID: " + file.Id);
Hope this helps.
I have an aspx page that I am displaying image from codehind c#.
Every item has it's own directory with images inside if there were any uploaded.
It works fine except if the directory doesn't exist for a certain item, I get a return error "Could not find part of the path....
In some cases the directory will not exist because there are no images assigned to the item. What should I include in my code to ignore this if there is no directory for the item?
Below is the code used to display the images:
string[] filePaths = Directory.GetFiles(Server.MapPath("/Test/Files/Item" + ItemNumber + "/"));
List<ListItem> files = new List<ListItem>();
foreach (string filePath in filePaths)
{
string fileName = Path.GetFileName(filePath);
files.Add(new ListItem(fileName, "/Test/Files/Item" + ItemNumber + "/" + fileName));
}
Use File.Exists method to filter out image files that are missing:
foreach (string filePath in filePaths.Where(File.Exists)) {
... //
}
You need to add using System.Linq and using System.IO in order for the above to compile.
Note: The above uses method groups for creating lambda expressions. Where(File.Exists) is a shorthand syntax for Where(f => File.Exists(f))
I'm trying to make a program that stores files as *.txt based documents. I want to be able to click a button and pull up a list of currently stored files
(Located in C:\ProgramData\ProgramName\Incidents)
Above is an example of what I'm trying to accomplish where 140219-000727 is the name of the file, the rest isn't need. Clicking Open or Double Clicking would "Open" that file and parse the .txt into pre-existing forms on a WinForm application that I have already created.
What is the best way to go about doing this with a minimal hit on system resources?
I think Directory.GetFiles is what you are looking for. You can use the simplest mask "*.txt" to fetch all txt files and then using Path.GetFileName cut the file name from the full path.
And later (on double click or button click) use the directory name + file name for opening:
//populating:
var files = Directory.GetFiles(YOUR_FOLDER_PATH, "*.txt");
foreach (var file in files)
{
var fileName = Path.GetFileName(file);
//assuming ListBox:
listBox.Items.Add(filename);
}
//opening (from listbox)
var fileName = Path.Combine(YOUR_FOLDER_PATH, listBox.SelectedItem.ToString());
File.ReadAllText(fileName);
You just need a FolderBrowserDialog control.
var fileNames = new List<string>();
var fileContents = new Dictionary<string, string>();
var filePaths = Directory.EnumerateFiles(folderBrowserDialog1.SelectedPath, "*.txt");
foreach (var filePath in filePaths)
{
var fileName =new FileInfo(filePath).Name;
fileNames.Add(fileName);
fileContents.Add(fileName, File.ReadAllText(filePath));
}