How to upload an IFormFile with MS Graph SDK - c#

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

Related

Extract embedded files from azure blob in c#

I have embedded pdf files stored inside a blob file.I want to extract those file from my blob.
below are the thing I have done so far:
I have made http trigger function app
establish connection with the storage container
able to fetch the blob.
get the embedded file I am using following code:
namespace PDFDownloader {
public static class Function1 { [FunctionName("Function1")]
public static async Task <IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log) {
log.LogInformation($"GetVolumeData function executed at:
{DateTime.Now}");
try {
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(Parameter.ConnectionString);
CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer cloudcontainer = cloudBlobClient.GetContainerReference(Parameter.SuccessContainer);
BlobResultSegment resultSegment = await
cloudcontainer.ListBlobsSegmentedAsync(currentToken: null);
IEnumerable <IListBlobItem> blobItems = resultSegment.Results;
string response = "";
int count = 0;
//string blobName = "";
foreach(IListBlobItem item in blobItems) {
var type = item.GetType();
if (type == typeof(CloudBlockBlob)) {
CloudBlockBlob blob = (CloudBlockBlob) item;
count++;
var blobname = blob.Name;
// response = blobname;
response = blob.DownloadTextAsync().Result;
//response = blob.DownloadToStream().Result;
}
}
if (count == 0) {
return new OkObjectResult("Error : File Not Found !!");
} else {
return new OkObjectResult(Convert.ToString(response));
}
} catch(Exception ex) {
log.LogError($ " Function Exception Message: {ex.Message}");
return new OkObjectResult(ex.Message.ToString());
} finally {
log.LogInformation($"Function- ENDED ON : {DateTime.Now}");
}
}
}
how can I read embedded files from my blob file response and send it to http?
Apart from the fact that your code needs quite some cleanup and that you should read up on the proper use of async, I believe your actual issue is here:
FileStream inputStream = new FileStream(response, FileMode.Open);
The response object contains the text content of your blob that you downloaded earlier. the Filestream ctor, however, expects a path to a file. Since you do not have a file here, Filestream is not the right thing to use. Either download the blob as a temp file or even directly as a string
Plus, do yourself a favor and switch to the latest version of the Storage Blob SDK (https://github.com/Azure/azure-sdk-for-net/tree/master/sdk/storage/Azure.Storage.Blobs#downloading-a-blob).
Think the one blob is the equivalent of one file(check it here).
Check the line:
response = Convert.ToString(blob.DownloadTextAsync().Result);
Is the content of your blob a valid file path ?
Maybe you are not using correctly the constructor of FileStream class public FileStream (string path, System.IO.FileMode mode). This constructor can throw a lot of different exceptions, try to find yours.
Also, as it was recommended in a previous answer, it is worth using the Azure.Storage.Blobs package based on SDK version 12, now you are using the SDK version 11(Microsoft.Azure.Storage.Blob).
using Bytescout.PDFExtractor;
var stream1 = await blob.OpenReadAsync(); //read your blob like
this
attachmentExtractor extractor = new AttachmentExtractor();
extractor.RegistrationName = "demo";
extractor.RegistrationKey = "demo";
// Load sample PDF document
extractor.LoadDocumentFromFile(stream1);
for (int i = 0; i < extractor.Count; i++)
{
Console.WriteLine("Saving attachment: " +
extractor.GetFileName(i));
// Save attachment to file
extractor.Save(i, extractor.GetFileName(i));
Console.WriteLine("File size: " + extractor.GetSize(i));
}
extractor.Dispose();*/

How to upload files to one drive from .NET

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);

Upload same documents to Dropbox .NET SDK

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.

Xamarin C# Android - converting .3gpp audio to bytes & sending to parseObject

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);

Upload file to skydrive through SkyDrive API

I try to upload a text file to my skydrive or at least create new text file in SD and edit it's content, through SkyDrive API in my Windows 8 application.
How can I do that?
I tried to do something like that:
LiveConnectClient client = new LiveConnectClient(session);
var fileData = new Dictionary<string, object>();
fileData.Add("name", "new_file.txt");
try
{
LiveOperationResult fileOperationResult = await client.PutAsync("me/skydrive", fileData);
this.infoTextBlock.Text = fileOperationResult.ToString();
}
catch (LiveConnectException exception)
{
this.infoTextBlock.Text = exception.Message;
}
but I get error
"The provided request is not valid. The root SkyDrive folder cannot be updated."
If I write something like "me/skydrive/" I get
"The provided URL is not valid. The requested path '' is not supported".
Method LiveConnectClient.PutAsync allows me only to update existing properties (but not it's content).
How it should be done properly?
Btw - Is content on LCDC(http://msdn.microsoft.com/en-us/library/live/hh826531.aspx) updated? I'm asking because some methods, which are in documentation, doesn't exist in dlls (f.e. LiveConnectClient.Upload. There's only BackgroundUploadAsync).
Thanks for help in advance,
Micheal
Close but as I wrote: I can't use client.upload method because LiveConnectClient class doesn't contain it. That's why I asked about site content update.
Anyway - I've got an answer:
//create a StorageFile (here is one way to do that if it is stored in your ApplicationData)
StorageFile file = awaitApplicationData.Current.LocalFolder.GetFileAsync("yourfilename.txt");
try {
client = new LiveConnectClient(session);
LiveOperationResult operationResult = await client.BackgroundUploadAsync("me/skydrive", file.Name, file, OverwriteOption.Overwrite);
}
catch (LiveConnectException exception) {
//handle exception
}
You should use the Upload method on LiveConnectionClient. For example, see the Uploading Files example in the Live SDK. Something like ...
LiveOperationResult fileOperationResult =
await client.Upload("me/skydrive", /*file name here*/, /*file stream here*/);
Here's another way to upload a file from a console application using a SkyDriveApiClient downloaded from http://skydriveapiclient.codeplex.com/releases/view/103081
static void Main(string[] args)
{
var client = new SkyDriveServiceClient();
client.LogOn("YourEmail#hotmail.com", "password");
WebFolderInfo wfInfo = new WebFolderInfo();
WebFolderInfo[] wfInfoArray = client.ListRootWebFolders();
wfInfo = wfInfoArray[0];
client.Timeout = 1000000000;
string fn = #"test.txt";
if (File.Exists(fn))
{
client.UploadWebFile(fn, wfInfo);
}
}

Categories

Resources