I need to maintain a historical record of certain documents, my initial solution was to copy them to a shared folder from .NET, but that doesn't seem so safe to me. Can I make the upload of those files to One Drive by using .NET with C#? If so, I would like documentation about it, I've already done a free search and I haven't found anything that could satisfy my needs. I apologize if the question is way too vague. Thank you.
Maybe this can help you out:
Using the code:
public string OneDriveApiRoot { get; set; } = "https://api.onedrive.com/v1.0/";
Upload file to OneDrive
//this is main method of upload file to OneDrive
public async Task<string> UploadFileAsync(string filePath, string oneDrivePath)
{
//get the upload session,we can use this session to upload file resume from break point
string uploadUri = await GetUploadSession(oneDrivePath);
//when file upload is not finish, the result is upload progress,
//When file upload is finish, the result is the file information.
string result = string.Empty;
using (FileStream stream = File.OpenRead(filePath))
{
long position = 0;
long totalLength = stream.Length;
int length = 10 * 1024 * 1024;
//In one time, we just upload a part of file
//When all file part is uploaded, break out in this loop
while (true)
{
//Read a file part
byte[] bytes = await ReadFileFragmentAsync(stream, position, length);
//check if arrive file end, when yes, break out with this loop
if (position >= totalLength)
{
break;
}
//Upload the file part
result = await UploadFileFragmentAsync(bytes, uploadUri, position, totalLength);
position += bytes.Length;
}
}
return result;
}
private async Task<string> GetUploadSession(string oneDriveFilePath)
{
var uploadSession = await AuthRequestToStringAsync(
uri: $"{OneDriveApiRoot}drive/root:/{oneDriveFilePath}:/upload.createSession",
httpMethod: HTTPMethod.Post,
contentType: "application/x-www-form-urlencoded");
JObject jo = JObject.Parse(uploadSession);
return jo.SelectToken("uploadUrl").Value<string>();
}
private async Task<string> UploadFileFragmentAsync(byte[] datas, string uploadUri, long position, long totalLength)
{
var request = await InitAuthRequest(uploadUri, HTTPMethod.Put, datas, null);
request.Request.Headers.Add("Content-Range", $"bytes {position}-{position + datas.Length - 1}/{totalLength}");
return await request.GetResponseStringAsync();
}
Get Share Link: ( Javascript )
//This method use to get ShareLink, you can use the link in web or client terminal
public async Task<string> GetShareLinkAsync(string fileID, OneDriveShareLinkType type, OneDrevShareScopeType scope)
{
string param = "{type:'" + type + "',scope:'" + scope + "'}";
string result = await AuthRequestToStringAsync(
uri: $"{OneDriveApiRoot}drive/items/{fileID}/action.createLink",
httpMethod: HTTPMethod.Post,
data: Encoding.UTF8.GetBytes(param),
contentType: "application/json");
return JObject.Parse(result).SelectToken("link.webUrl").Value<string>();
}
From: https://code.msdn.microsoft.com/office/How-to-upload-file-to-21125137
An easy solution for beginners:
Install OneDrive app on your computer then login to your account and set a folder to it.
Then you just need to copy your files in the folder you set in the last step. It would be synced automatically by OneDrive application.
File.Copy(sourceFileFullPath,OneDriveFileFullPath);
Related
I have put together the following function to upload a file from the web server to OneDrive with MS Graph. However, I am no longer saving files to the server and would rather just take the IFormFile from my users form submit and then upload that straight to OneDrive. However, my function uses a path on the web server. How do I convert this to use IformFile directly without saving it to a physical server path?
public async Task<String> UploadFileAsync(string DriveID, string SourceFile, string DestinationPath)
{
try
{
using var fileStream = System.IO.File.OpenRead(SourceFile);
// Use properties to specify the conflict behavior
// in this case, replace
var uploadProps = new DriveItemUploadableProperties
{
AdditionalData = new Dictionary<string, object>
{
{ "#microsoft.graph.conflictBehavior", "fail" }
}
};
// Create the upload session
// itemPath does not need to be a path to an existing item
var uploadSession = await _graphServiceClient.Me.Drives[DriveID].Root
.ItemWithPath(DestinationPath)
.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);
var totalLength = fileStream.Length;
// Create a callback that is invoked after each slice is uploaded
IProgress<long> progress = new Progress<long>(prog => {
Console.WriteLine($"Uploaded {prog} bytes of {totalLength} bytes");
});
// Upload the file
var uploadResult = await fileUploadTask.UploadAsync(progress);
Console.WriteLine(uploadResult.UploadSucceeded ?
$"Upload complete, item ID: {uploadResult.ItemResponse.Id}" :
"Upload failed");
return uploadResult.ItemResponse.Id;
}
catch (ServiceException ex)
{
Console.WriteLine($"Error uploading: {ex.ToString()}");
return "Failed: " + ex.ToString();
}
}
This creates the file no problem and returns the
I have the following code:
private static void Download(DropboxClient client, string dropboxfolder, string localfolder, FileMetadata file)
{
//Console.WriteLine("Download file...");
var task1 = client.Files.DownloadAsync(dropboxfolder + "/" + file.Name);
task1.Wait();
using (var response = task1.Result)
{
Console.WriteLine("Downloaded {0} Rev {1}", response.Response.Name, response.Response.Rev);
Console.WriteLine("------------------------------");
using (Stream output = File.OpenWrite(Path.Combine(localfolder, response.Response.Name)))
{
var task2 = response.GetContentAsStreamAsync();
task2.Wait();
using (Stream input = task2.Result)
{
input.CopyTo(output);
}
}
Console.WriteLine("------------------------------");
}
}
how do I modify it such that the output file will have the same timestamp as the file in dropbox.
[EDIT] I know how to set a date of a file in C#, but I don't know how to get that timestamp from dropbox in order to call that method. The question I am asking is more generic than that because perhaps there is an option on the dropbox API that allows the file to be set by dropbox.
When you use DownloadAsync to download a file, that also returns the FileMetadata for that file. You can check the FileMetadata.ServerModified or FileMetadata.ClientModified to get the modified time as desired.
You can find an example of getting the metadata here.
I'm trying out a few things with Blazor and I'm still new to it. I'm trying to get a file stream to download to the browser. What's the best way to download a file from Blazor to browser?
I've tried using a method in my razor view that returns a stream but that didn't work.
//In my Blazor view
#code{
private FileStream Download()
{
//get path + file name
var file = #"c:\path\to\my\file\test.txt";
var stream = new FileStream(test, FileMode.OpenOrCreate);
return stream;
}
}
The code above doesn't give me anything, not even an error
Another solution is to add a simple api controller endpoint using endpoints.MapControllerRoute. This will work only with server side blazor though.
Ex:
endpoints.MapBlazorHub();
endpoints.MapControllerRoute("mvc", "{controller}/{action}");
endpoints.MapFallbackToPage("/_Host");
Then add a controller. For example:
public class InvoiceController : Controller
{
[HttpGet("~/invoice/{sessionId}")]
public async Task<IActionResult> Invoice(string sessionId, CancellationToken cancel)
{
return File(...);
}
}
Usage in a .razor file:
async Task GetInvoice()
{
...
Navigation.NavigateTo($"/invoice/{orderSessionId}", true);
}
Although the above answer is technically correct, if you need to pass in a model -POST-, then NavigationManager won't work. In which case you, must likely end up using HttpClient component. If so wrap the response.Content -your stream- in a DotNetStreamReference instance - new DotNetStreamReference(response.Content). This will create a ReadableStream. Then create the blob with the content. Keep in mind DotNetStreamReference was recently introduced with .NET 6 RC1. As of now the most efficient way. Otherwise, you can use fetch API and create a blob from the response.
I wound up doing it a different way, not needing NavigationManager. It was partially taken from the Microsoft Docs here. In my case I needed to render an Excel file (using EPPlus) but that is irrelevant. I just needed to return a Stream to get my result.
On my Blazor page or component when a button is clicked:
public async Task GenerateFile()
{
var fileStream = ExcelExportService.GetExcelStream(exportModel);
using var streamRef = new DotNetStreamReference(stream: fileStream);
await jsRuntime.InvokeVoidAsync("downloadFileFromStream", "Actual File Name.xlsx", streamRef);
}
The GetExcelStream is the following:
public static Stream GetExcelStream(ExportModel exportModel)
{
var result = new MemoryStream();
ExcelPackage.LicenseContext = LicenseContext.Commercial;
var fileName = #$"Gets Overwritten";
using (var package = new ExcelPackage(fileName))
{
var sheet = package.Workbook.Worksheets.Add(exportModel.SomeUsefulName);
var rowIndex = 1;
foreach (var dataRow in exportModel.Rows)
{
...
// Add rows and cells to the worksheet
...
}
sheet.Cells.AutoFitColumns();
package.SaveAs(result);
}
result.Position = 0; // This is required or no data is in result
return result;
}
This JavaScript is in the link above, but adding it here as the only other thing I needed.
window.downloadFileFromStream = async (fileName, contentStreamReference) => {
const arrayBuffer = await contentStreamReference.arrayBuffer();
const blob = new Blob([arrayBuffer]);
const url = URL.createObjectURL(blob);
const anchorElement = document.createElement("a");
anchorElement.href = url;
anchorElement.download = fileName ?? "";
anchorElement.click();
anchorElement.remove();
URL.revokeObjectURL(url);
}
I have a web API function that successfully upload files to Dropbox (using their new .NET SDK) and then gets shared links to the uploaded files (each document a time).
private async Task<string> Upload(DropboxClient dbx, string localPath, string remotePath)
{
const int ChunkSize = 4096 * 1024;
using (var fileStream = File.Open(localPath, FileMode.Open))
{
if (fileStream.Length <= ChunkSize)
{
WriteMode mode = new WriteMode();
FileMetadata fileMetadata = await dbx.Files.UploadAsync(remotePath, body: fileStream, mode: mode.AsAdd, autorename: true);
//set the expiry date
var settings = new SharedLinkSettings(expires: DateTime.Today.AddDays(7));
SharedLinkMetadata sharedLinkMetadata = await dbx.Sharing.CreateSharedLinkWithSettingsAsync(fileMetadata.PathLower, settings);
return sharedLinkMetadata.Url;
}
else
{
await this.ChunkUpload(dbx, remotePath, fileStream, ChunkSize);
}
return "error";
}
}
That usually works fine but when I upload the exact same document (name and content) twice, nothing happens and I do need to have both files stored in my Dropbox account.
It can be as much same documents as needed (not only two), my best scenario would be to have the second document (and third etc..) automatically renamed and uploaded to Dropbox.
Any idea on how to accomplish that?
I post the answer, maybe it will help someone.. I spent long time till I figure it out.
This is the code that checks if a file already exists in Dropbox.
If the file exists it checks if a link was shared for this file and based on the result it generates/retrieves a/the shared link.
private async Task<string> Upload(DropboxClient dbx, string localPath, string remotePath)
{
const int ChunkSize = 4096 * 1024;
using (var fileStream = File.Open(localPath, FileMode.Open))
{
if (fileStream.Length <= ChunkSize)
{
WriteMode mode = new WriteMode();
FileMetadata fileMetadata = await dbx.Files.UploadAsync(remotePath, body: fileStream, mode: mode.AsAdd, autorename: true);
//set the expiry date
var existingDoc = await dbx.Files.GetMetadataAsync(remotePath);
if (existingDoc.IsFile)
{
var sharedLink = await dbx.Sharing.ListSharedLinksAsync(remotePath);
var settings = new ListSharedLinksArg(remotePath);
ListSharedLinksResult listSharedLinksResult = await dbx.Sharing.ListSharedLinksAsync(remotePath);
if (listSharedLinksResult.Links.Count > 0)
{
return listSharedLinksResult.Links[0].Url;
}
else
{
var sharedLinkSettings = new SharedLinkSettings(expires: DateTime.Today.AddDays(7));
SharedLinkMetadata sharedLinkMetadata = await dbx.Sharing.CreateSharedLinkWithSettingsAsync(remotePath, sharedLinkSettings);
return sharedLinkMetadata.Url;
}
}
else
{
var settings = new SharedLinkSettings(expires: DateTime.Today.AddDays(7));
SharedLinkMetadata sharedLinkMetadata = await dbx.Sharing.CreateSharedLinkWithSettingsAsync(fileMetadata.PathLower, settings);
return sharedLinkMetadata.Url;
}
}
else
{
var sharedLink = await this.ChunkUpload(dbx, remotePath, fileStream, ChunkSize);
return sharedLink;
}
}
When you upload the same exact content to the same path again, the Dropbox API won't produce a conflict or another copy of the file, as nothing changed. (Edit: you can force a conflict even for identical contents by using strictConflict=true, e.g., on UploadAsync).
If you want another copy of the same data in your account, you can specify the different desired path when calling UploadAsync the second time.
Or, more efficiently, you can use CopyAsync to make a copy of the file already on Dropbox.
I'm pretty new to processing recorded sound.
I can successfully record audio into a .3gpp file, save it locally on my mobile device, and play it back.
The trouble I'm having is that I want to convert the sound file into binary so that I can stick it into a parseObject, and upload the file to a cloud. I then want to be able to access that file from a separate device, and stream it.
--UPDATE--- I'm not using binary anymore, I'm using a parseFile object. I now just need to pull the object down from the cloud.
Here's the code I'm using to record the audio (working):
string sName;
string path = "/storage/extSdCard/";
string newPath = "";
_start.Click += delegate {
_stop.Enabled = !_stop.Enabled;
_start.Enabled = !_start.Enabled;
sName = _edittext.Text;
if (sName.Equals(" "))
{
}
else
{
//user enters a name for ther audio file
newPath = path + sName + ".3gpp";
_recorder.SetAudioSource (AudioSource.Mic);
_recorder.SetOutputFormat (OutputFormat.ThreeGpp);
_recorder.SetAudioEncoder (AudioEncoder.AmrNb);
_recorder.SetOutputFile (newPath);
_recorder.Prepare ();
_recorder.Start ();
}
};
_stop.Click += delegate {
_stop.Enabled = !_stop.Enabled;
_recorder.Stop ();
// _recorder.Reset ();
_player.SetDataSource (newPath);
_player.Prepare ();
_player.Start ();
};
Here is the class I'm using to send the data to a cloud - this is executed on the click of a button and works, it currently sends hard coded strings into an object which i can successfully retrieve.
HOWEVER, I want the binary string to go into the testObject["audio"], so I can retrieve it.
async Task sendToCloud()
{
ParseClient.Initialize ("--I've put my keys here but I'm censoring them--", "--I've put my keys here but I'm censoring them--");
try
{
byte[] data =null;
ParseFile file = null;
using (StreamReader reader = new StreamReader(LoadPath))
{
data = System.Text.Encoding.UTF8.GetBytes(reader.ReadToEnd());
file = new ParseFile("theaudio.3gpp", data);
}
Console.WriteLine("Awaiting reader");
await file.SaveAsync ();
var auidoParseObject = new ParseObject("AudioWithData");
//Console.WriteLine(ParseUser.getUserName());
auidoParseObject["userName"] = "userName";
auidoParseObject["file"] = file;
await auidoParseObject.SaveAsync();
}
Any help would be GREATLY APPRECIATED.
EDIT2:
I've made some progress, I'm struggling, however, to get the audio file down from the cloud still.
here's my new code:
async Task sendToCloud(string filename)
{
ParseClient.Initialize ("censored", "censored");
var testObject = new ParseObject ("Audio");
string LoadPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData);
string savetheFile = sName + ".3gpp";
string tempUserName;
LoadPath += savetheFile;
Console.WriteLine ("loadPath: " + LoadPath);
try
{
byte[] data = File.ReadAllBytes(LoadPath);
ParseFile file = new ParseFile(savetheFile, data);
await file.SaveAsync();
var auidoParseObject = new ParseObject("AudioWithData");
//Console.WriteLine(ParseUser.getUserName());
if (ParseUser.CurrentUser != null)
{
tempUserName = ParseUser.CurrentUser.Username.ToString();
}
else{
tempUserName = "Anonymous";
}
//tempUserName = ParseUser.CurrentUser.Username.ToString();
Console.WriteLine("PARSE USERNAME: " + tempUserName);
auidoParseObject["userName"] = tempUserName;
auidoParseObject["userName"] = tempUserName;
auidoParseObject["file"] = file;
await auidoParseObject.SaveAsync();
}
catch (Exception e)
{
Console.WriteLine("Failed to await audio object! {0}" + e);
}
}
So currently, my objects have the following structure:
"auidoParseObject" contains two children: username (string) and file (ParseFile object)
"file" has two children: the name of the audio (entered by the user -string), and the data in bytes.
I need the audio to be placed into a mdeiaplayer player object, and played.
In the long run, I'll want to extract:
(forgive my pseudo-SQL, but I don't understand the querying documentation):
Select (all files) from (audioParseObject) where (the username = current user.username)
AND THEN
put those files into a listview
user selects a file from the listview and it plays.
ANY help or pointers would be great.
Thanks.
Parse has really good docs on their site with examples - have you read them?
First, you would use a ParseFile (not a ParseObject) to save your file
byte[] data = File.ReadAllBytes(path_to_your_file);
ParseFile file = new ParseFile("your_file_name_and_ext", data);
await file.SaveAsync();
After you save your file, you can add a reference to it in your ParseObject
testObject ["audio"] = file;
To retrieve the file later, you use the Url property from the ParseFile.
var file = testObject.Get<ParseFile>("audio");
byte[] data = await new HttpClient().GetByteArrayAsync(file.Url);