I have a Problem with my Azure Function. I use a Linux based App service Plan (B1). After deploying my Code and running the Azure function I get this Error.
2020-03-11T07:02:55.985 [Error] Executed 'PdfRender_dotnet_framework' (Failed, Id=52201ad7-8012-4f93-bc17-0accae6a1540)
No such file or directory
The strange thing about this is that I can change to this Directory in the console and in Kudu with bash/ssh. So it seems that everything was deployed fine, but why is it still showing me this error message.
I checked many times if everything was deployed right and i didn't find any issues.
Error Message
Directory in Console
Directory in Bash (Kudu)
This is my function.json
I also noticed that the azure function reach the code.
My Function Code.
public static class dotnet_core_pdf
{
[FunctionName("dotnet_core_pdf")]
public static HttpResponseMessage Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequest req, TraceWriter log, ExecutionContext executionContext)
{
string name = req.Query["url"];
log.Info(name);
//Initialize HTML to PDF converter
HtmlToPdfConverter htmlConverter = new HtmlToPdfConverter();
WebKitConverterSettings settings = new WebKitConverterSettings();
//Set WebKit path
settings.WebKitPath = Path.Combine(executionContext.FunctionAppDirectory, "QtBinariesWindows");
//Assign WebKit settings to HTML converter
htmlConverter.ConverterSettings = settings;
//Convert URL to PDF
PdfDocument document = htmlConverter.Convert(name);
MemoryStream ms = new MemoryStream();
//Save the PDF document
document.Save(ms);
ms.Position = 0;
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new ByteArrayContent(ms.ToArray());
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "HTMLToPDFAzure.pdf"
};
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
return response;
}
}
}
NOTICE: I changed the folders Name, I am using .Net Core not .Net Framework
Related
I have a function app running in azure and I have wrapped the response to be returned as httpResponseMessage:
public static HttpResponseMessage CreateOkRequestMessageResponse(
this HttpRequestMessage req,
string fileName,
bool cache = false,
TimeSpan? maxAge = null)
{
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StreamContent(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose));
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = Path.GetFileName(fileName),
};
result.Headers.Add("Cache-Control", cache ? $"public, max-age={maxAge.GetValueOrDefault().TotalSeconds}" : "no-cache");
return result;
}
When I inspect the calls to the service directly using Postman I can see e.g.:
Then, when I call it via CDN I see:
Which I assume means that it did not return from the cache but instead called the service again.
After a few calls I get finally the response looking like this:
Which is finally is cached.
The CDN is setup using bicep template:
Is there something missing to have CDN work as expected?
The call to the service takes around 3 minutes and I have run multiple calls in a row with the same request but it is still not being returned from the cache.
I want to create endpoint that will stream video stream that is stored in azure CloudBlob. Here is snippet of my code:
public async Task<IActionResult> GetVideo(string videoId)
{
var videoStream = await _contentStorage.Get(videoId);
var fileStreamResult = new FileStreamResult(videoStream, mimeType);
fileStreamResult.EnableRangeProcessing = true;
return fileStreamResult;
}
and in ContentStorage
public async Task<StoredContent> Get(string id)
{
var block = _blobContainer.GetBlobClient(id);
var ms = await block.OpenReadAsync();
return ms;
}
I had everything working fine except iPhones and safari, after some debugging it turned out that my endpoint is returning 200 http code, but it should 206 - partial content. So I made some changes into my code, here is some snippet:
public async Task<IActionResult> GetVideo(string videoId)
{
var videoStream = await _contentStorage.Get(videoId);
var ms = new MemoryStream();
await videoStream.CopyToAsync(ms);
var fileStreamResult = new FileStreamResult(ms, mimeType);
fileStreamResult.EnableRangeProcessing = true;
return fileStreamResult;
}
Now when I test it on iphone or by postman response is 206, and it's working fine. But I thing that copping the video stream into new memorystream is a valid approach.
Correct me if I'm wrong but I understand this code as for every partial of the video, I'm downloading whole video from blob storage, cut it and then return just the piece within range.
It's not sure for me how to handle this case, is there any out of the box solution for that, or do I need to read range header from request and use OpenReadAsync with parameters as position and buffer side? Or there is another way?
Solution for me was to update Azure.Storage.Blobs library. I had 12.6.0 and after update to 12.7.0 it started working as expected, since they added:
Added seekability to BaseBlobClient.OpenRead().
I want to create an Azure function that streams mp4 files from Azure Blob Storage to clients such as the HTML video tag, using request range headers and partial responses (http status code 206s). I am using an Azure function so that I can implement required security checks later.
The FileStreamResult class can handle the range requests from the html video tag automatically, so the following works nicely to stream a video from the local file system to the browser, allowing the user to scan forwards/backwards through the video at will:
[FunctionName("StreamVideo")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)]
HttpRequest req, ILogger log)
{
// This stream is seekable!
Stream str = File.OpenRead($#"C:\temp\BigBuckBunny.mp4");
return new FileStreamResult(str, new MediaTypeHeaderValue("video/mp4").MediaType)
{
EnableRangeProcessing = true,
};
}
I want to do exactly this, but using a file that sits in Azure Blob Storage. The following code does not work. It returns the whole file in a single response with http status code 200. Presumably this is because the stream is not seekable?
[FunctionName("StreamVideo")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)]
HttpRequest req, ILogger log)
{
string containerName = req.Query["container"];
string blobName = req.Query["blob"];
var blobServiceClient = new BlobServiceClient("CONNECTION_STRING_HERE", new BlobClientOptions(BlobClientOptions.ServiceVersion.V2019_12_12));
BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(containerName);
BlobClient blob = containerClient.GetBlobClient(blobName);
// This stream is NOT seekable!
Stream str = await blob.OpenReadAsync();
return new FileStreamResult(str, new MediaTypeHeaderValue("video/mp4").MediaType)
{
EnableRangeProcessing = true,
};
}
How can I get this to work? Note that I have implemented a similar solution using Web Api for .NET Framework 4.x which uses PushStreamContent with the same media files so I know the mp4 files in Blob Storage are okay. My function targets .NET Core 3.1 and I'm testing it all locally at the moment.
I just checked your code, and seems no error there. And here're something you can check.
1.Nav to azure blob storage, and check if the video's content-type is video/mp4 or not.
2.For MediaTypeHeaderValue namespace, I'm using this one: using System.Net.Http.Headers;
3.And here is the packages I'm using:
<PackageReference Include="Azure.Storage.Blobs" Version="12.6.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.7" />
And in my side, I can see the response code is actually 206. The screenshot is as below:
I finally got this to work using the following solution. This results in 206 responses from my Azure Function with payloads as defined by the chunkSize variable (in this case 896.2kB).
[FunctionName("StreamVideo")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)]
HttpRequest req, ILogger log)
{
string containerName = req.Query["container"];
string blobName = req.Query["blob"];
var blobServiceClient = new BlobServiceClient("CONNECTION_STRING_HERE",
new BlobClientOptions(BlobClientOptions.ServiceVersion.V2019_12_12));
BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(containerName);
BlobClient blob = containerClient.GetBlobClient(blobName);
Response<BlobProperties> blobPropertiesResponse = await blob.GetPropertiesAsync();
long blobFileLength = blobPropertiesResponse.Value.ContentLength;
long chunkSize = 1000000;
string[] rangeHeaders = req.Headers["Range"][0].Substring(6).Split("-");
long start = int.Parse(rangeHeaders[0]);
long end = string.IsNullOrEmpty(rangeHeaders[1])
? Math.Min(start + chunkSize, blobFileLength - 1)
: Math.Min(start + chunkSize, long.Parse(rangeHeaders[1]));
Stream stream = await blob.OpenReadAsync(start);
var httpResponseMessage = new HttpResponseMessage
{
Content = new StreamContent(stream), StatusCode = HttpStatusCode.PartialContent,
};
httpResponseMessage.Content.Headers.ContentLength = end - start + 1;
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("video/mp4");
httpResponseMessage.Content.Headers.ContentRange = new ContentRangeHeaderValue(start, end, blobFileLength);
httpResponseMessage.Headers.AcceptRanges.Add("bytes");
httpResponseMessage.StatusCode = HttpStatusCode.PartialContent;
return httpResponseMessage;
}
And this is the Firefox network output:
I've been running into issues when downloading Excel .xlsx files using Google Drive Api v3. The code I'm using is as follows (I'm using the .NET SDK):
using Google.Apis.Auth.OAuth2;
using Google.Apis.Drive.v3;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace DriveQuickstart
{
class Program
{
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/drive-dotnet-quickstart.json
static string[] Scopes = { DriveService.Scope.Drive };
static string ApplicationName = "Drive API .NET Quickstart";
const string FileId = "my_file_id"; //put the ID of the Excel file you want to download here
public static void Main(string[] args)
{
Run().GetAwaiter().GetResult();
Console.Read();
}
private static async Task Run()
{
UserCredential credential;
using (var stream =
new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
{
// The file token.json stores the user's access and refresh tokens, and is created
// automatically when the authorization flow completes for the first time.
string credPath = "token.json";
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Console.WriteLine("Credential file saved to: " + credPath);
}
// Create Drive API service.
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
// Define parameters of request.
FilesResource.GetRequest getRequest = service.Files.Get(FileId);
using (var stream = new System.IO.FileStream("anExcelFile.xlsx", System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite))
{
var downloadProgress = await getRequest.DownloadAsync(stream, CancellationToken.None);
if (downloadProgress.Exception != null)
{
Console.WriteLine(string.Format("We got error {0} {1} {2}", downloadProgress.Exception.Message, Environment.NewLine, downloadProgress.Exception.StackTrace));
}
else
{
Console.WriteLine("Download ok");
}
}
}
}
}
You can run this sample easily by following the steps described here. This works fine, however, as soon as someone opens the file with Google Sheets and modifies it, I start seeing the following error
D2020-03-16 02:10:13.647293 Response[00000007] Response status: InternalServerError 'Internal Server Error'
D2020-03-16 02:10:13.653278 Response[00000007] An abnormal response wasn't handled. Status code is InternalServerError
D2020-03-16 02:10:13.660288 Response[00000007] Abnormal response is being returned. Status Code is InternalServerError
E2020-03-16 02:10:13.667240 Exception occurred while downloading media The service drive has thrown an exception: Google.GoogleApiException: Internal Server Error
at Google.Apis.Download.MediaDownloader.<DownloadCoreAsync>d__31.MoveNext()
Looking at the file info after it was open with Google sheets, I can see that its size is changed to 0, so I tried to export it as you would for a Google spreadsheet, like so:
FilesResource.ExportRequest exportRequest = client.Files.Export(fileId, mimeType);
using (var stream = new System.IO.FileStream(fileName, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite))
{
await exportRequest.DownloadAsync(stream, cancellationToken);
}
With mimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
However, I then run in the following error:
D2020-03-16 01:53:13.512928 Response[00000003] Response status: Forbidden 'Forbidden'
D2020-03-16 01:53:13.520906 Response[00000003] An abnormal response wasn't handled. Status code is Forbidden
D2020-03-16 01:53:13.525911 Response[00000003] Abnormal response is being returned. Status Code is Forbidden
E2020-03-16 01:53:13.538857 Exception occurred while downloading media The service drive has thrown an exception: Google.GoogleApiException: Google.Apis.Requests.RequestError
Export only supports Google Docs. [403]
Errors [
Message[Export only supports Google Docs.] Location[ - ] Reason[fileNotExportable] Domain[global]
]
at Google.Apis.Download.MediaDownloader.<DownloadCoreAsync>d__31.MoveNext()
So it seems that neither downloading nor exporting is working in this particular case. Anything else I should be trying ? Using the webContentLink (https://drive.google.com/uc?id=fileId&export=download) works fine (in a browser that is) so I guess it should be possible to download the file.
I raised the issue to Google and it seems it was fixed (cf this issue). I tried again today and following the steps described in the original question, I now can see that after the Excel file has been edited with Google sheets, its size is now greater than 0 and it can be downloaded.
Files that couldn't be downloaded because of this issue still appear to have the same problem but deleting/reuploading manually these files should make them downloadable.
I can't use the ServiceStack Client libraries and I've chosen to use the HttpClient PCL library instead. I can do all my Rest calls (and other json calls) without a problem, but I'm now stucked with uploading files.
A snippet of what I am trying to do:
var message = new HttpRequestMessage(restRequest.Method, restRequest.GetResourceUri(BaseUrl));
var content = new MultipartFormDataContent();
foreach (var file in files)
{
byte[] data;
bool success = CxFileStorage.TryReadBinaryFile(file, out data);
if (success)
{
var byteContent = new ByteArrayContent(data);
byteContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = System.IO.Path.GetFileName(file) ,
};
content.Add(byteContent);
}
}
message.Content = content;
Problem is now that I get a null reference exception (status 500) when posting. I doesn't get into the service. I see the call in the filterrequest, but that's it.
So I'm wondering what I do wrong and how I can pinpoint what is going wrong. How can I catch the correct error on the ServiceStack layer?