How to adjust DateModified property on a single OneDrive item - c#

I am able to upload a file using the OneDrive SDK no problem. As per the information on the OneDrive Dev Center, The FileSystemInfo.DateModified refers to the time the file was seen by the service, rather than when it was modified locally.
I am attempting to manually alter it to the local value with the suggestion to include them with the request, but the value being set in my code is not sticking and is returning to the time when the PutAsync<Item> request is completed. What Am I doing wrong?
My code:
if (localfile != null)
{
localprop = await localfile.GetBasicPropertiesAsync();
localtime = localprop.DateModified;
try
{
Stream syncstream = await localfile.OpenStreamForReadAsync();
using (syncstream)
{
var upload = await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Content.Request().PutAsync<Item>(syncstream);
upload.FileSystemInfo.LastModifiedDateTime = localtime;
}
}
catch (OneDriveException)
{ }
}
My query against the same:
oneDItem = await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Request().GetAsync();
var oneDtime = (DateTimeOffset)oneDItem.FileSystemInfo.LastModifiedDateTime;

If you upload a file to one drive, there is no parameter for LastModifiedDateTime to request together, you may not change the modified time when uploading. But you can update an item metadata by update request. After uploading, you can get the item you just uploaded and update its LastModifiedDateTime meta data. Code as follows:
if (localfile != null)
{
var localprop = await localfile.GetBasicPropertiesAsync();
var localtime = localprop.DateModified;
try
{
Stream syncstream = await localfile.OpenStreamForReadAsync();
using (syncstream)
{
DriveItem upload = await _userDrive.Me.Drive.Root.ItemWithPath("regfolder/regdata.jpg").Content.Request().PutAsync<DriveItem>(syncstream);
DriveItem updateitem = new DriveItem() {
FileSystemInfo=new Microsoft.Graph.FileSystemInfo()
{
LastModifiedDateTime = localtime
}
};
DriveItem Updated = await _userDrive.Me.Drive.Root.ItemWithPath("regfolder/regdata.jpg").Request().UpdateAsync(updateitem);
}
}
catch (Exception ex)
{ }
}

Related

Downloading attachments with in sub folders using graph api and C#

I'm creating a solution to download attachments from an outlook365 account. The inbox of this account has subfolders and folders within the subfolders. The structure looks like below.
I've been trying to download the attachments but I'm only able to get the attachment from the messages in the main inbox.
But I want to look into the subfolders. So by studying the Microsoft graph document (https://learn.microsoft.com/en-us/graph/api/attachment-get?view=graph-rest-1.0&tabs=http#http-request) I found the below HTTP URL request.
GET /me/mailFolders/{id}/childFolders/{id}/.../messages/{id}/attachments/{id}
GET /users/{id | userPrincipalName}/mailFolders/{id}/childFolders/{id}/messages/{id}/attachments/{id}
GET /me/mailFolders/{id}/childFolders/{id}/.../messages/{id}/attachments/{id}/$value
GET /users/{id | userPrincipalName}/mailFolders/{id}/childFolders/{id}/messages/{id}/attachments/{id}/$value
I'm pretty new to C# I'm having a hard time converting above mentioned URL method to C#. Below is the current code I use to grab the messages and expand the attachments.
public static async Task<IMailFolderMessagesCollectionPage> GetAttachmentToday()
{
var DateToDay = DateTime.Now.ToString("dd.MM.yyyy");
var SearchOption = new List<QueryOption>
{
new QueryOption("search", $"%22received:{DateToDay}%22")
};
try
{
var attachments = await graphClient.Me.MailFolders.Inbox.Messages
.Request(SearchOption)
.Top(5)
.Select(a => new
{
a.Subject,
a.HasAttachments
})
.Expand("attachments")
.GetAsync();
return attachments;
}
catch(ServiceException ex)
{
Console.WriteLine($"Error getting events: {ex.Message}");
return null;
}
}
I can't figure out how to implement the Microsoft document URL suggestion to work with var attachments = await graphClient.Me.MailFolders.Inbox.Messages. Hope someone can point me down the right path.
I figure this out and used the example HTML code given by Microsoft documents. Which is below.
GET /me/mailFolders/{id}/childFolders/{id}/.../messages/{id}/attachments/{id}
GET /users/{id | userPrincipalName}/mailFolders/{id}/childFolders/{id}/messages/{id}/attachments/{id}
GET /me/mailFolders/{id}/childFolders/{id}/.../messages/{id}/attachments/{id}/$value
GET /users/{id | userPrincipalName}/mailFolders/{id}/childFolders/{id}/messages/{id}/attachments/{id}/$value
So based on the above code I came up with the below C# code.
//To get the first supfolder and it's ID.
var FirstSubFolderIDs = await graphClient.Me.MailFolders["Inbox"].ChildFolders
.Request()
.Select(fid => new
{
fid.Id,
fid.DisplayName
})
.GetAsync();
foreach(var FirstSubFolderID in FirstSubFolderIDs)
{
if(FirstSubFolderID.Id != null)
{ //Using the above ID to get the second folder and it's ID.
var SecondSubFolderIDs = await graphClient.Me.MailFolders["Inbox"]
.ChildFolders[$"{FirstSubFolderID.Id}"]
.ChildFolders
.Request()
.Select(sid => new
{
sid.Id,
sid.DisplayName
})
.GetAsync();
foreach (var SecondSubFolderID in SecondSubFolderIDs)
{
if(SecondSubFolderID.Id != null)
{ //Continuing on using the first 2 ID's to get the 3rd folder and it's ID.
var ThirdSubFolderIDs = await graphClient.Me.MailFolders["Inbox"]
.ChildFolders[$"{FirstSubFolderID.Id}"]
.ChildFolders[$"{SecondSubFolderID.Id}"]
.ChildFolders
.Request()
.Select(tid => new
{
tid.Id,
tid.DisplayName,
})
.GetAsync();
foreach (var ThirdSubFolderID in ThirdSubFolderIDs)
{
if(ThirdSubFolderID.DisplayName == "New")
{ //Now you're at the last folder where the emails are at.
var GetMessageAttachments = await graphClient.Me.MailFolders["Inbox"]
.ChildFolders[$"{FirstSubFolderID.Id}"]
.ChildFolders[$"{SecondSubFolderID.Id}"]
.ChildFolders[$"{ThirdSubFolderID.Id}"]
.Messages
//.Request(SearchOption)
.Request()
.Expand("attachments")
.Select(gma => new
{
gma.Id,
gma.Subject,
gma.HasAttachments,
gma.Attachments
})
.GetAsync();
//Get Message count that includes attachments
var MessageCount = GetMessageAttachments.Count;
if (MessageCount != 0)
{
//Do what you want here
}
Decided to answer my question so someone else might find this helpful.

How to upload file in Sharepoint using .net core with C#?

I want to upload file in my sharepoint using .net core c#, but I would't find the microsoft dll or CSOM package in .net core. PLease let me know if you have something that can work in .net core with C#.
Microsoft haven't released official .NET Core CSOM Library, please see the UserVoice, the status is still "Working On it":
Support .NET Core with CSOM
Nowadays, it's suggested to use .NET Standard platform instead if possible
Sounds like what you are looking for is Microsoft Graph API.
I chose to use their .NET SDK which for implementation looked like this in C#:
/// <summary>
/// Takes a path that includes the expected file and uploads it to the specified location.
/// Takes the byte array in the DriveItemRequest Content property and streams it
/// to SharePoint. Returns true if successful. Maximum file upload is 34MB.
/// </summary>
/// <param name="request">DriveItemRequest</param>
/// <returns>bool</returns>
public async Task<bool> UploadSharePointItem(DriveItemRequest request)
{
var result = false;
if (!string.IsNullOrEmpty(request.SiteId) &&
!string.IsNullOrEmpty(request.DriveId) &&
!string.IsNullOrEmpty(request.Paths[0]) &&
request.Content != null)
{
using (var fileStream = new MemoryStream(request.Content))
{
var response = new DriveItem();
// small downloads
if (request.Content.Length <= 4000000) // 4MB
{
response = await _client.Sites[request.SiteId].Drives[request.DriveId]
.Root.ItemWithPath(request.Paths[0]).Content.Request()
.PutAsync<DriveItem>(fileStream);
result = response.Size > 0 ? true : false;
}
// large downloads
else if (request.Content.Length < 34000000) // 34MB
{
// https://learn.microsoft.com/en-us/graph/sdks/large-file-upload?tabs=csharp
// Use properties to specify the conflict behavior
var uploadProps = new DriveItemUploadableProperties
{
AdditionalData = new Dictionary<string, object>
{
{ "#microsoft.graph.conflictBehavior", "replace" }
}
};
// Create the upload session
var uploadSession = await _client.Sites[request.SiteId]
.Drives[request.DriveId].Root.ItemWithPath(request.Paths[0])
.CreateUploadSession(uploadProps).Request().PostAsync();
// Max slice size must be a multiple of 320 KiB
int maxSliceSize = 320 * 1024;
var fileUploadTask = new LargeFileUploadTask<DriveItem>
(uploadSession, fileStream, maxSliceSize);
try
{
// Upload the file
var uploadResult = await fileUploadTask.UploadAsync();
response = uploadResult.ItemResponse;
result = uploadResult.UploadSucceeded;
}
catch (ServiceException ex)
{
Console.WriteLine($"Error uploading: {ex}");
}
}
else
{
return result;
}
// check-in item
await _client.Sites[request.SiteId].Drives[request.DriveId]
.Items[response.Id].Checkin().Request().PostAsync();
// update size
if (response.Size == 0)
{
response.Size = request.Content.Length;
await _client.Sites[request.SiteId].Drives[request.DriveId]
.Items[response.Id].Request().UpdateAsync(response);
}
}
}
return result;
}
Links:
https://learn.microsoft.com/en-us/graph/sdks/create-requests?tabs=CS#make-a-post-request-to-create-a-new-entity
Upload file to SharePoint drive using Microsoft Graph

Error during storage blob copy operation - The requested operation is not allowed in the current state of the entity

I am having below method to copy data to destination storage blob
private static async Task MoveMatchingBlobsAsync(IEnumerable<ICloudBlob> sourceBlobRefs,
CloudBlobContainer sourceContainer,
CloudBlobContainer destContainer)
{
foreach (ICloudBlob sourceBlobRef in sourceBlobRefs)
{
if (sourceBlobRef.Properties.ContentType != null)
{
// Copy the source blob
CloudBlockBlob destBlob = destContainer.GetBlockBlobReference(sourceBlobRef.Name);
try
{
//exception throwed here - StartCopyAsync
await destBlob.StartCopyAsync(new Uri(GetSharedAccessUri(sourceBlobRef.Name, sourceContainer))); /
ICloudBlob destBlobRef = await destContainer.GetBlobReferenceFromServerAsync(sourceBlobRef.Name);
while (destBlobRef.CopyState.Status == CopyStatus.Pending)
{
Console.WriteLine($"Blob: {destBlobRef.Name}, Copied: {destBlobRef.CopyState.BytesCopied ?? 0} of {destBlobRef.CopyState.TotalBytes ?? 0}");
await Task.Delay(500);
destBlobRef = await destContainer.GetBlobReferenceFromServerAsync(sourceBlobRef.Name);
}
Console.WriteLine($"Blob: {destBlob.Name} Complete");
}
catch (Exception e)
{
Console.WriteLine($"Blob: {destBlob.Name} Copy Failed");
}
}
}
}
I am getting below exception, there is no more information
The requested operation is not allowed in the current state of the entity
What may be the cause?
Here is my method to collect blob from the source location
private static async Task<IEnumerable<ICloudBlob>> FindMatchingBlobsAsync(CloudBlobContainer blobContainer,string prefix, int maxrecords,int total)
{
List<ICloudBlob> blobList = new List<ICloudBlob>();
BlobContinuationToken token = null;
do
{
BlobResultSegment segment = await blobContainer.ListBlobsSegmentedAsync(prefix: prefix, useFlatBlobListing: true, BlobListingDetails.None, maxrecords, token, new BlobRequestOptions(), new OperationContext());
token = segment.ContinuationToken;
foreach (var item in segment.Results)
{
blobList.Add((ICloudBlob)item);
if (blobList.Count > total) // total record count is configured
token = null;
}
} while ( token != null);
return blobList;
}
Here is my GetSharedAccessUri method which returns Uri without any issue
private static string GetSharedAccessUri(string blobName, CloudBlobContainer container)
{
DateTime toDateTime = DateTime.Now.AddMinutes(60);
SharedAccessBlobPolicy policy = new SharedAccessBlobPolicy
{
Permissions = SharedAccessBlobPermissions.Read,
SharedAccessStartTime = null,
SharedAccessExpiryTime = new DateTimeOffset(toDateTime)
};
CloudBlockBlob blob = container.GetBlockBlobReference(blobName);
string sas = blob.GetSharedAccessSignature(policy);
return blob.Uri.AbsoluteUri + sas;
}
This will iterate only 2 levels but not dynamically till the inner levels. I have blob in below hierarchy
--Container
--FolderA
--FolderAA
--FolderAA1
--File1.txt
--File2.txt
--FolderAA2
--File1.txt
--File2.txt
--FolderAA3
--FolderAB
--File8.txt
--FolderAC
--File9.txt
This hierarchy is dynamic
Additional Question: Is there any GUI tool to copy blob data to target storage account?
UPDATE
According to your description, I modified it in the official sample code. It is already possible to completely copy the data in one container to another account, and the code has been uploaded to Github.
To use this sample code, you need to modify the App.Config file. Formal use to the production environment needs to be perfected.
https://github.com/Jason446620/BlobContainerCopy
PRIVIOUS
You can refer to the code in this post for copy operation. If the solution in this post does not help you, please let me know and I will continue to follow up to help you solve the problem.
And u can download Azure Storage Explorer is the GUI tool to copy datas.

PlanGrid API - Publish Log still prompts for version set name after UploadVersion

I'm trying to post a file to my PlanGrid project using the following code. Once the upload is complete, I log into the website, open the Publish Log, then click on "Publish Your Sheets", at which point it asks me again to define version set. Can someone clarify what the UploadVersionRequest.VersionName property is used for then?
public static async Task Upload(string project_uid, string filename, Stream payload)
{
var api = PlanGridClient.Create(Properties.Settings.Default.ApiKey);
var versionRequest = new UploadVersionRequest
{
NumberOfFiles = 1,
VersionName = "MyVersion" // how does this get used??
};
var versionUpload = await api.UploadVersion(project_uid, versionRequest);
foreach (var fileUploadRequest in versionUpload.FileUploadRequests)
{
var uploadFile = new UploadFile
{
FileName = filename
};
var fileUpload = await api.UploadFileToVersion(project_uid, versionUpload.Uid, fileUploadRequest.Uid, uploadFile);
await api.Upload<object>(fileUpload, payload);
}
await api.CompleteVersionUpload(project_uid, versionUpload.Uid);
}
Thanks for the question. We released an update to the upload process that included both version name and issue date. That change has not been reflected in the API, so when you set the version name in the API, it is not reflected during the publishing process.

In C#, how do I compare the datemodified property of a local file with an Amazon S3 file?

I've got some local files that need to be updated from S3 occasionally. I'd prefer to update them only if the S3 version is newer. I can't quite understand how to use the
ModifiedSinceDate
property in S3 Objects. In fact, I'm not sure whether to use Metadata, or if there is something else I'm supposed to be checking.
I'm imagining something like this:
GetObjectRequest request = new GetObjectRequest().WithBucketName(my_bucketName).WithKey(s3filepath);
using (GetObjectResponse response = client.GetObject(request))
{
string s3DateModified = response.ModifiedSinceDate; // Complete psuedo code
DateTime s3_creationTime = DateTime.Convert(s3DateModified); //Complete psuedo code
DateTime local_creationTime = File.GetCreationTime(#"c:\file.txt");
if (s3_creationTime > local_CreationTime)
{
//download the S3 object;
}
}
Thanks so much!
EDIT---------------------------------------
I think I'm almost there... I'm writing the date modified as a meta-tag when I originally upload the object:
PutObjectRequest titledRequest = new PutObjectRequest();
.WithFilePath(savePath)
.WithBucketName(bucketName)
.WithKey(tempFileName)
.WithMetaData("Last-Modified", System.DateTime.Now.ToString());
Then using this for the check/download:
using (GetObjectResponse response = client.GetObject(request))
{
string lastModified = response.Metadata["x-amz-meta-last-modified"];
DateTime s3LastModified = Convert.ToDateTime(lastModified);
string dest = Path.Combine(#"c:\Temp\", localFileName);
DateTime localLastModified = File.GetLastWriteTime(dest);
if (!File.Exists(dest))
{
response.WriteResponseStreamToFile(dest);
}
if (s3LastModified > localLastModified)
{
response.WriteResponseStreamToFile(dest);
}
}
Something's breaking, I think perhaps the WriteResponseStream is asynchronous, and it's trying two respnse.WriteResponseStreamtToFile(dest) at the same time. Anyways, I'll update this if I can get it nailed down.
You should use GetObjectMetadata to retrieve information about an object without actually downloading the object itself.
The GetObjectMetadataResponse class has a LastModified property or you could use custom Metadata I guess.
Then you would use GetObject to actually download and save the file.
Something like:
using (GetObjectMetadataResponse response = client.GetObjectMetadata(request))
{
DateTime s3LastModified = response.LastModified;
string dest = Path.Combine(#"c:\Temp\", localFileName);
DateTime localLastModified = File.GetLastWriteTime(dest);
if (!File.Exists(dest) || s3LastModified > localLastModified)
{
// Use GetObject to download and save file
}
}

Categories