creating CSV file in local drive with asp.net in azure - asp.net-mvc

I have a webapp in which i can create csv file and locate it in my c drive,
It works fine when running locally, but once i deploy the application to Azure
I'm getting :
UnauthorizedAccessException: Access to the path 'C:\Tuesday_HH19_MI6.csv' is denied.
How can i allow the website to access and create a file in the user's local drive?
I attached the entire log exception from azure if this helps,
Thank you

A Web App running in Azure can't directly save it to your user's local drive, but it can generate the CSV and then prompt them to download it via the browser. You can use a few options depending on if you are trying to send that already exists on the filesystem or if you have generated it dynamically and have it as a byte array or stream.
Here are some sample controller methods to give you and idea. Your controller could be doing a bunch of stuff before the return statement, these examples are simplified.
Existing file on filesystem, use a FileResult:
public FileResult DownloadFile()
{
// create the file etc and save to FS
return File("/Files/File Result.pdf", "text/csv", "MyFileName.csv");
}
If the file is generated in memory and you have it as a byte array:
public FileContentResult DownloadContent()
{
// Create CSV as byte array
var myfile = MyMethodtoCreateCSV();
return new FileContentResult(myfile, "text/csv") {
FileDownloadName = "MyFileName.csv"
};
}

Related

Azure + ASP.Net + System.UnauthorizedAccessException: Access to the path '/Content/img/CourseImages' is denied

I am trying to upload an file using asp.net on azure (its not VM).
But constantly, i am receiving below error:
System.UnauthorizedAccessException: Access to the path '/Content/img/CourseImages' is denied.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.Directory.InternalCreateDirectory(String fullPath, String path, Object dirSecurityObj, Boolean checkHost)
at System.IO.Directory.InternalCreateDirectoryHelper(String path, Boolean checkHost)
at System.IO.Directory.CreateDirectory(String path)
Here is my code snippet:
string courseImageFolderPath = ConfigurationManager.AppSettings["CourseImage"];
string courseImageFilePath = Path.Combine(courseImageFolderPath, fileName);
if (!Directory.Exists(courseImageFolderPath))
Directory.CreateDirectory(courseImageFolderPath);
courseImage.SaveAs(System.Web.Hosting.HostingEnvironment.MapPath(courseImageFilePath));
Every Azure Web App has a local directory(D:\local) which is temporary. The content in this folder will be deleted when the run is no longer running on the VM. This directory is a place to store temporary data for the application. It is not recommended to use this folder by your web application.
According to Azure Web App sandbox, I suggest you create a temp folder at the root of your web application folder(D:\home\site\wwwroot) and use it to store the temp data. Or as jayendran said, you could use blob storage to upload your image.
string tempFolder = Server.MapPath("~/TEMP");
if (!Directory.Exists(tempFolder))
{
Directory.CreateDirectory(tempFolder);
}
For more detials, you could refer to this issue.
Also, it seems that the app service do not get access to network resource. So what you would do is impersonate a user that has access on the network resource in order to create the directory. Please refer to this article.

Setting GOOGLE_APPLICATION_CREDENTIALS for an MVC site hosted on azure

Title says it all pretty much.
I tried uploading the json file to azure storage and referenced it's url when setting the GOOGLE_APPLICATION_CREDENTIALS environment variable under app settings, but when remotely debugging the site, apparently the url/directory was not in an acceptable format. I can’t store the json file locally either because the website doesn’t have any idea about my C drive directories.
Where should I store this file so that I can set the GOOGLE_APPLICATION_CREDENTIALS environment variable for my azure site to the directory of the json file?
The ToChannelCredentials() approach does not seem to work anymore, so I come up with an other solution that works on Azure. I create a text file in the /bin folder of my Azure server with the credentials and then I point the environment variable to this file. Google Cloud API will use this for the default credentials.
string json = #"{
'type': 'service_account',
'project_id': 'xxx',
'private_key_id': 'xx',
'private_key': 'xxx',
...
}"; // this is the content of the json-credentials file from Google
// Create text file in projects bin-folder
var binDirectory = Path.GetDirectoryName(Assembly.GetCallingAssembly().CodeBase);
string fullPath = Path.Combine(binDirectory, "credentials.json").Replace("file:\\","");
using (StreamWriter outputFile = new StreamWriter(fullPath, false)) {
outputFile.WriteLine(json);
}
// Set environment variabel to the full file path
Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", fullPath);
// Now you can call the service and it will pick up your credentials
TranslationServiceClient client = TranslationServiceClient.Create();
If anyone is wondering how to handle the Google's credentials smoothly in .Net applications instead of strange way of using the file on server, this is how I solved it for Translation Service. Other services must follow same principle:
store the content of the Google credentials json file as an environment variable in settings.json/azure configuration for your app (using ' ' instead of " " for inner text):
"GOOGLE_APPLICATION_CREDENTIALS": "{'type': 'service_account','project_id': ...}"
create and return the client:
var credential = GoogleCredential.FromJson(Environment.GetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS"));
var channelCredentials = credential.ToChannelCredentials();
var channel = new Channel(TranslationServiceClient.DefaultEndpoint.ToString(), channelCredentials);
return TranslationServiceClient.Create(channel);
Took a while for me to figure it our. Hope it helps.
I use the .json file in my local environment (because of environment variable length limit in Windows) and on Azure I use an "Application setting" to set an environment variable. This code handles both cases:
string? json;
var filename = Environment.GetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS");
if (filename != null)
{
json = System.IO.File.ReadAllText(filename);
}
else
{
json = Environment.GetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS_STRING");
if (json == null)
{
throw new Exception(
"GOOGLE_APPLICATION_CREDENTIALS_STRING environment variable with JSON is not set");
}
}
var credential = GoogleCredential.FromJson(json).ToChannelCredentials();
var grpcChannel = new Channel("firestore.googleapis.com", credential);
var grcpClient = new Firestore.FirestoreClient(grpcChannel);
var firestoreClient = new FirestoreClientImpl(grcpClient, FirestoreSettings.GetDefault());
return await FirestoreDb.CreateAsync(FirebaseProjectId, firestoreClient);
Was looking how to set the "GOOGLE_APPLICATION_CREDENTIALS" in Azure App Service. The answers here didn't help me. My solution is very simple without any code change.
In the configuration of the app service go to the Path Mappings
Add a New Azure Storage Mount. eg /mounts/config
Add the credentials.json file to the file share
In the application settings, add the GOOGLE_APPLICATION_CREDENTIALS and set the value to: /mounts/config/credentials.json
That is all.
In the azure app on the azure portal go to application settings and add the credentials under application settings tab
Then you can reference them in your code as they were in your web.config file.

Azure download blob to users computer

I was working on my download blob function when I ran into some problems..
I want the user to be able to download a blob and I want a specific filename on that item when its downloaded to the users computer. I also want the user to decide which folder the item should be saved to.
This is my not so good looking code so far:
var fileName = "tid.txt9c6b412a-270a-4f67-8e65-7ce2bf87503d";
var containerName = "uploads";
CloudStorageAccount account = CloudStorageAccount.DevelopmentStorageAccount;
var blobClient = account.CreateCloudBlobClient();
var container = blobClient.GetContainerReference(containerName);
var blob = container.GetBlockBlobReference(fileName);
using (var filestream = System.IO.File.OpenWrite(#"C:\Info\tid.txt9c6b412a-270a-4f67-8e65-7ce2bf87503d"))
{
blob.DownloadToStream(filestream);
}
fileName = the blob name
Is it possible to change the name? The file ending gets all messed up with my guid.
At the moment the download to folder is C:\Info.. How would this work when the website is published? How can I let the user decide which folder the item should be saved to? Am i doing this right?
thank you in advance
/Filip
How would this work when the website is published?
Slow for the user and expensive for you. You are streaming the BLOB through your app, so you'll bottleneck. Use Shared Access Signatures and download the blob directly from the browser. Use Content-Disposition as part of the URL to have the browser prompt the user with a Save As dialog. See Javascript download a URL - Azure Blob Storage.
Your question: Is it possible to change the name?
The name of the blob and the name on the user's disk are your/his choice. There is no need for them to match, except perhaps to avoid confusion. On the off chance that your user will upload it again (with changes, perhaps?) save some metadata so the original file and the updated file can be related in blob storage.
Once you execute the line:
var blob = container.GetBlockBlobReference(fileName);
... you have told Azure all it needs to know to locate the blob.
In the line:
using (var filestream = System.IO.File.OpenWrite...
... you tell your code where to put the file on the disk. You say it's a website, so this statement will put the file onto the web server's disk, not your user's. To get the file onto the user's disk, you need one more step - download the file from the web server (web role instance) to your user's computer. You can give him control of the folder and file name. Here is the relevant section in MSDN:
Downloading and Uploading Files
Is this download function acceptable? Slow/expensive or is it as good as it gets?
public void DownloadFile(string blobName)
{
CloudBlobContainer blobContainer = CloudStorageServices.GetCloudBlobsContainer();
CloudBlockBlob blob = blobContainer.GetBlockBlobReference(blobName);
MemoryStream memStream = new MemoryStream();
blob.DownloadToStream(memStream);
Response.ContentType = blob.Properties.ContentType;
Response.AddHeader("Content-Disposition", "Attachment; filename=" + blobName.ToString());
Response.AddHeader("Content-Length", (blob.Properties.Length - 1).ToString());
Response.BinaryWrite(memStream.ToArray());
Response.End();
}

uploading a file from iOS mobile application to SharePoint

I'm working on a SharePoint mobile solution where I'm using the web services exposed in server/_vti_bin/sitedata.asmx, server/_vti_bin/Lists.asmx and server/_vti_bin/copy.asmx.
I'm able to successfully fetch the list of sites, document libraries and files using the services defined in server/_vti_bin/sitedata.asmx.
Now I'm actually trying to upload an image file from Photo Albums available in iOS to SharePoint. For this, I tried using CopyIntoItems web service, where in I'm getting the following error response.
<CopyResult ErrorCode="DestinationInvalid" ErrorMessage="The Copy web service method must be called on the same domain that contains the destination url." DestinationUrl="http://xxxxserveripxxxxxx/Shared Documents/image1.png"/>
But came to know that this service is used only if the file to be uploaded is also from the same source(i.e., from sharepoint).
Is there any other way to upload a file available in iPhone to SharePoint.
Also tired addAttachment service defiend in server/_vti_bin/Lists.asmx but I'm unable to identify the input parameters which requires list name and list Item ID.
I'm trying to upload a file to Shared Documents, so I've List Name value which is the one in curly braces of Shared Documents but now what should be the List Item Id value?
These are the details I've with regard to "Shared Documents" document library.
{
AllowAnonymousAccess = false;
AnonymousViewListItems = false;
BaseTemplate = DocumentLibrary;
BaseType = DocumentLibrary;
DefaultViewUrl = "/Shared Documents/Forms/AllItems.aspx";
Description = "Share a document with the team by adding it to this document library.";
InheritedSecurity = true;
InternalName = "{425F837A-F110-4876-98DE-C92902446935}";
LastModified = "2013-07-26 20:09:58Z";
ReadSecurity = 1;
Title = "Shared Documents";
},
So, I'm using the using InternalName value for listName tag.
What should be the value of listItemID?
Am I going in the right way or is there any other approach to upload a local file from mobile to SharePoint?
Thanks
Sudheer
Are you actually calling a URL or are you using the IP (you x'ed it out and said server IP)? If you don't have Alternate Access Mappings defined for the IP, uploads will fail but the GET requests will generally work ok.

Get full path of a file stored in sharepint documentlibrary

I have a file stored in a sharepoint libarary like
filePathAndName = "http://spstore/sites/appsitename/documentlibraryname/abc.xls"
I need to be able to open the the abc.xls file using
byte[] buffer = System.IO.File.ReadAllBytes(filePathAndName);
but i get an error stating. uri formats are not supported. How do I get the full path to the file?
You have to download the file first. For example you could use a WebClient to send an HTTP request to the remote server and retrieve the file contents:
using (var client = new WebClient())
{
byte[] file = client.DownloadData("http://spstore/sites/appsitename/documentlibraryname/abc.xls");
// TODO: do something with the file data
}

Resources