I'm learning Azure storage development and I'm trying to create a container and then upload a file to it. I created a console app and am calling the upload method from Main. Here is the first half of the upload method that creates the container.
public async static Task<bool> UploadToAzure()
{
string containerName = "sample-"+Guid.NewGuid().ToString();
var connectionString = "XXXXXXXXXXXXXXXXXXXXX";
var blobServiceClient = new BlobServiceClient(connectionString);
BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(containerName);
try
{
var test = blobServiceClient.GetBlobContainers();
foreach (var container in test)
{
Console.WriteLine(container.Name);
}
var result = await containerClient.CreateIfNotExistsAsync(Azure.Storage.Blobs.Models.PublicAccessType.BlobContainer);
var containerProp = await containerClient.GetPropertiesAsync();
Console.WriteLine(containerProp.Value);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message+"\n}");
}
I started with just calling the containerclient.CreateIfNotExistsAnsync() and it wouldn't create the container. On a whim, I added the blobServiceClient.GetBlobContainers() and now it will create the container. I can comment out blobServiceClient.GetBlobContainers() and it will not create the container. I can put it after the create method and it still won't work. That's the first issue.
The second one is, it exits the code when inside the containerclient.CreateIfNotExistsAnsync() method. It will create the folder but never hit the next line of code. I get a message in the console that it "exited with code 0. Press any key to close this window...". It doesn't hit the Catch or display any kind of error, it just exists.
I got this pattern from my Udemy course and I've read a number of articles online and these seem to be the right steps. Any suggestions are appreciated.
Thanks
After reproducing from our end, The code you were using was working for us until we found that you have missed using await while calling UploadToAzure().
Below is the code that we used
namespace ConsoleApplication
{
class Program
{
static async Task Main(string[] args)
{
await UploadToAzure();
}
public async static Task<bool> UploadToAzure()
{
string containerName = "sample-" + Guid.NewGuid().ToString();
var connectionString = "<YOUR_COONECTION_STRING>";
var blobServiceClient = new BlobServiceClient(connectionString);
BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(containerName);
await containerClient.CreateIfNotExistsAsync(Azure.Storage.Blobs.Models.PublicAccessType.BlobContainer);
return true;
}
}
}
else you can completely remove async and have
namespace CreateContainer
{
class Program
{
static async Task Main(string[] args)
{
UploadToAzure();
}
public static bool UploadToAzure()
{
string containerName = "sample-" + Guid.NewGuid().ToString();
var connectionString = "<YOUR_COONECTION_STRING>";
var blobServiceClient = new BlobServiceClient(connectionString);
BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(containerName);
containerClient.CreateIfNotExists(Azure.Storage.Blobs.Models.PublicAccessType.BlobContainer);
return true;
}
}
}
NOTE: It is preferable to use the async method since you might face some issues if you are dealing with large volumes of data.
Related
I'm wondering whether there is a clean way to create a new file using Octokit in case the file specified doesn't exist, or otherwise create it? I'm currently attempting to do this via a try-catch block. However, there needs to be a more straightforward way to do this except a try-catch block?
private IRepositoryContentsClient Content => _client.Repository.Content;
public void TransmitLog(string logFilePath)
{
var logContent = LogFileToString(logFilePath);
var githubPath = GenerateGithubPath(logFilePath);
try
{
UpdateLog(githubPath, logContent);
}
catch (NotFoundException)
{
CreateLog(githubPath, logContent);
}
catch (AggregateException)
{
CreateLog(githubPath, logContent);
}
}
private void UpdateLog(string githubPath, string logContent)
{
// MY APP FAILS HERE
var existingFile = Content.GetAllContentsByRef(
_owner, _repo, githubPath, _branch).Result;
// update the file
var relevantSha = existingFile.First().Sha;
var updateRequest = new UpdateFileRequest("Log update" + DateTime.UtcNow, logContent, relevantSha, _branch);
var updateChangeSet = Content.UpdateFile(_owner, _repo, githubPath, updateRequest);
}
private void CreateLog(string githubPath, string logFileContent)
{
// if file is not found, create it
var createRequest =
new CreateFileRequest("Log Creation" + DateTime.UtcNow, logFileContent, _branch);
var createChangeSet = Content.CreateFile(_owner, _repo, githubPath, createRequest);
}
EDIT: I initially had both CreateLog and UpdateLog declared as async void, which led to a whole set of other problems. I edited that out since what I'm really interested in is how to avoid this try/catch structure.
This may not be 100% an EPPlus issue, but since it is Blazor WASM it appears I cannot get the file path to a static image in the wwwroot/images folder. I can get the url and paste it into a browser and that works, even adding that same path to the src attribute of an img works, neither of those helps me.
FYI "background" in this context means a watermark.
It appears that the EPPlus dev team only wants a drive path the file (ex. C:\SomeFolder\SomeFile.png), and I am not seeing how to get that within Blazor WASM. I can get the bytes of the file in c# and even a stream, but no direct path.
My code is the following:
using (var package = new ExcelPackage(fileName))
{
var sheet = package.Workbook.Worksheets.Add(exportModel.OSCode);
sheet.BackgroundImage.SetFromFile("https://localhost:44303/images/Draft.png");
...
}
This returns an exception:
Unhandled exception rendering component: Can't find file /https:/localhost:44303/images/Draft.png
Noticing that leading / I even tried:
sheet.BackgroundImage.SetFromFile("images/Draft.png");
Which returned the same error:
Unhandled exception rendering component: Can't find file /images/Draft.png
So, I am perhaps needing 1 of 2 possible answers:
A way to get a local drive path to the file so the .SetFromFile method is not going to error.
To have a way to set that BackgroundImage property with a byte array or stream of the image. There is this property BackgroundImage.Image but it is readonly.
Thanks to a slap in the face from #Panagiotis-Kanavos I wound up taking the processing out of the client and moving it to the server. With that, I was able to use Static Files to add the watermark with relatively little pain.
In case anyone may need the full solution (which I always find helpful) here it is:
Here is the code within the button click on the Blazor component or page:
private async Task GenerateFile(bool isFinal)
{
...
var fileStream = await excelExportService.ProgramMap(exportModel);
var fileName = "SomeFileName.xlsx";
using var streamRef = new DotNetStreamReference(stream: fileStream);
await jsRuntime.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef);
}
That calls a client-side service that really just passes control over to the server:
public class ExcelExportService : IExcelExportService
{
private const string baseUri = "api/excel-export";
private readonly IHttpService httpService;
public ExcelExportService(IHttpService httpService)
{
this.httpService = httpService;
}
public async Task<Stream> ProgramMap(ProgramMapExportModel exportModel)
{
return await httpService.PostAsJsonForStreamAsync<ProgramMapExportModel>($"{baseUri}/program-map", exportModel);
}
}
Here is the server-side controller that catches the call from the client:
[Route("api/excel-export")]
[ApiController]
public class ExcelExportController : ControllerBase
{
private readonly ExcelExportService excelExportService;
public ExcelExportController(ExcelExportService excelExportService)
{
this.excelExportService = excelExportService;
}
[HttpPost]
[Route("program-map")]
public async Task<Stream> ProgramMap([FromBody] ProgramMapExportModel exportModel)
{
return await excelExportService.ProgramMap(exportModel);
}
}
And that in-turn calls the server-side service where the magic happens:
public async Task<Stream> ProgramMap(ProgramMapExportModel 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.OSCode);
if (!exportModel.IsFinal)
{
var pathToDraftImage = #$"{Directory.GetCurrentDirectory()}\StaticFiles\Images\Draft.png";
sheet.BackgroundImage.SetFromFile(pathToDraftImage);
}
...
sheet.Cells.AutoFitColumns();
package.SaveAs(result);
}
result.Position = 0; // Without this, data does not get written
return result;
}
For some reason, this next method was not needed when doing this on the client-side but now that it is back here, I had to add a method that returned a stream specifically and used the ReadAsStreamAsync instead of ReadAsJsonAsync:
public async Task<Stream> PostAsJsonForStreamAsync<TValue>(string requestUri, TValue value, CancellationToken cancellationToken = default)
{
Stream result = default;
var responseMessage = await httpClient.PostAsJsonAsync(requestUri, value, cancellationToken);
try
{
result = await responseMessage.Content.ReadAsStreamAsync(cancellationToken: cancellationToken);
}
catch (HttpRequestException e)
{
...
}
return result;
}
Lastly, in order for it to give the end-user a download link, this was used (taken from the Microsoft Docs):
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'm creating a discord bot through discord's open source library. Here is how all of my commands get created:
private async Task HandleCommandAsync(SocketMessage arg)
{
var message = arg as SocketUserMessage;
if(message is null || message.Author.IsBot)
return;
int argPos = 0;
if(message.HasStringPrefix("!", ref argPos) || message.HasMentionPrefix(client.CurrentUser, ref argPos))
{
var context = new SocketCommandContext(client, message);
var result = await commands.ExecuteAsync(context, argPos, services);
if(!result.IsSuccess)
Console.WriteLine(result.ErrorReason);
}
}
And here is my problem command:
public class AddArchiveCommand : ModuleBase<SocketCommandContext>
{
DiscordSocketClient client;
SocketUserMessage msg;
List<Archive> archiveList;
public AddArchiveCommand(DiscordSocketClient client, SocketUserMessage msg, List<Archive> archiveList)
{
this.archiveList = archiveList;
}
[Command("addarchive")]
public async Task AddArchiveAsync(ITextChannel sourceChan, ITextChannel destChan)
{
Archive archive = new Archive(sourceChan.Id, destChan.Id, archiveList);
archive.AddArchive(); //Using custom method rather than the list add because it check for more logic.
await ReplyAsync("Now archiving " + sourceChan.Name + " to " + destChan.Name);
}
}
I'm trying to pass the List archiveList variable through a constructor from my main class into the command in order to add an archive request to this list. Problem is, I'm not able to do something as simple as "var example = new AddArchiveCommand(archiveList)" due to the nature of how the SocketCommandContext class works.
What am I missing here? Thank you for any explanation. I've been stuck on this for weeks and just need to work with someone on a solution.
I have an Azure WebJob which has a similar code inside:
public class Functions
{
public static void GenerateImagesForViewer(
[QueueTrigger("resize-images-queue")] BlobInformation blobInfo,
[Blob("unprocessed-pdf-storage-container/{BlobName}", FileAccess.Read)] Stream input,
[Blob("unprocessed-pdf-storage-container/{BlobNameWithoutExtention}-pdf.jpg")] CloudBlockBlob outputPdf)
{
//Do something here
string connectionString = "myConnectionString";
TopicClient Client =
TopicClient.CreateFromConnectionString(connectionString, "resize-
images-topic");
var topicMessage = new BrokeredMessage(blobInfo);
Client.Send(topicMessage);
}
public static void GenerateImagesForViewerW80(
[ServiceBusTrigger("resize-images-topic", "SizeW80")] BlobInformation blobInfo,
[Blob("unprocessed-pdf-storage-container/{BlobNameWithoutExtention}-pdf.jpg", FileAccess.Read)] Stream input,
[Blob("processed-image-storage-container/{BlobNameWithoutExtention}-h0-w80.jpg")] CloudBlockBlob outputBlob_0_80)
{
// It never comes here
//Do something here
}
}
After uploading data (BlobInformation object) to my Queue there is no problem triggering the first method (GenerateImagesForViewer). But when I try to send data (BlobInformation object) to the topic it never triggers any of the subscribers(GenerateImagesForViewerW80). Is there something wrong in the code, or there is a required configuration in Azure?
In Program.cs, config.UseServiceBus(); is necessary for usage of ServiceBus trigger. We won't see warning if there are other trigger or bindings in Functions, like your case.
See code sample below and check official guidance for more details.
var config = new JobHostConfiguration();
if (config.IsDevelopment)
{
config.UseDevelopmentSettings();
}
config.UseServiceBus();
var host = new JobHost(config);
host.RunAndBlock();
Besides, I see some suspicious blank in your input and output blob path. If it's the same as your original code, just remove them otherwise the trigger won't execute code related to blob operation correctly.
Currently, I make a GetObjectMetaDataRequest, if the GetObjectMetaDataResponse throw an exception means the object doesn't exist. Is there a better way to check whether the file exists without downloading the file.
you can use S3FileInfo class and Exists method of this class it will hep you to check if file exists without download the file .see the example below I used the AWSSDK 3.1.6 .net(3.5) :
public static bool ExistsFile()
{
BasicAWSCredentials basicCredentials = new BasicAWSCredentials("my access key", "my secretkey");
AmazonS3Config configurationClient = new AmazonS3Config();
configurationClient.RegionEndpoint = RegionEndpoint.EUCentral1;
try
{
using (AmazonS3Client clientConnection = new AmazonS3Client(basicCredentials, configurationClient))
{
S3FileInfo file = new S3FileInfo(clientConnection, "mybucket", "FolderNameUniTest680/FileNameUnitTest680");
return file.Exists;//if the file exists return true, in other case false
}
}
catch(Exception ex)
{
return false;
}
}
If you are in a similar situation as myself and are using .Net Core and don't have access to Amazon.S3.IO (and S3FileInfo method), you can do the following using asynchronous GetObjectMetadataRequest method:
static private AmazonS3Client s3Client = new AmazonS3Client();
public static async Task<bool> FileExistsS3Async(string _bucket, string _key)
{
GetObjectMetadataRequest request = new GetObjectMetadataRequest()
{
BucketName = _bucket,
Key = _key
};
try
{
await s3Client.GetObjectMetadataAsync(request);
return true;
}
catch (AmazonS3Exception exception)
{
return false;
}
}
This function has worked for me when calling within a Unity game. You can also call the above function synchronously using the following:
bool exists = Task.Run(()=>FileExistsS3Async(_bucket, _key)).Result;
Try this solution, it works for me.
AmazonS3Client client = new AmazonS3Client(accessKey, secretKey, regionEndpoint);
S3FileInfo s3FileInfo = new S3FileInfo(client, bucketName, fileName);
return s3FileInfo.Exists;
There is no ListObjectRequest, but instead a ListObjectsRequest where you cannot specify the Key. You then have to go through all the objects to find the one you want. I am currently looking in to it since I seem to get time out errors whilst downloading the file. (If anyone has some idea how to solve that feel free to comment).
You could instead try the List Parts Request if you happen to know the upload id.
Other than that I have no idea. Would like to have a chat with the person who wrote the S3 api...
You're probably going to have to use the REST API yourself, as the method suggested, internally just does exactly the same thing (try...catch on the request)
You can use this code to check whether an object exist in S3 or not:
public class S3CheckFileExists
{
private readonly IAmazonS3 amazonS3;
public S3CheckFileExists(IAmazonS3 amazonS3)
{
this.amazonS3 = amazonS3;
}
public async Task<bool> S3ObjectExists(string bucketName, string keyLocation)
{
var listS3Objects = await this.amazonS3.ListObjectsV2Async(new ListObjectsV2Request
{
BucketName = bucketName,
Prefix = keyLocation, // eg myfolder/myimage.jpg (no / at start)
MaxKeys = 1
});
if (listS3Objects.S3Objects.Any() == false || listS3Objects.S3Objects.All(x => x.Key != keyLocation))
{
// S3 object doesn't exist
return false;
}
// S3 object exists
return true;
}
}
You'll need to register IAmazonS3 in your IoC (aka services) container though:
services.AddAWSService<IAmazonS3>();
Yes.
You can use a ListObjectsRequest. Use the Marker property, and retrieve only 1 element.