I have a .NET 6 Web API project with DinkToPdf which I use on macOS on my dev machine, and two servers on Linux Ubuntu 20.4 and Windows Server 2012. My API detects OS on which it runs and uses the corresponding library to convert HTML to PDF file on output.
My controller:
public class ClientDocController : BaseController
{
private readonly IClientAppointmentDocumentService _clientAppointmentDocumentService;
private readonly IConverter _htmlToPdfConverter;
public ClientDocController(IClientAppointmentDocumentService clientAppointmentDocumentService, IConverter htmlToPdfConverter)
{
_clientAppointmentDocumentService = clientAppointmentDocumentService;
_htmlToPdfConverter = htmlToPdfConverter;
}
[HttpGet("{documentId}/pdf/")]
[RestApiAuthorize(AccountRolePermissions.VIEW_CLIENT_DOCUMENTS)]
public async Task<IActionResult> GetPdfAsync([FromRoute] int documentId, bool uploadToClientPortal, int? templateId = null,
bool clientPrint = false, bool sendToClientEmail = false, CancellationToken cancellationToken = default)
{
try
{
var htmlDocument = templateId == null
? await _clientAppointmentDocumentService.GetDefaultHtmlDocumentAsync(documentId, clientPrint, cancellationToken)
: await _clientAppointmentDocumentService.GetHtmlDocumentAsync(documentId, templateId, clientPrint, cancellationToken);
var fileName = $"Document_{documentId}_{DateTime.Now:s}.pdf";
var conversionSettings = PdfConfigurationDefaults.GetDefaultSettings(htmlDocument); //this get individual settings for each platform
var pdf = _htmlToPdfConverter.Convert(conversionSettings);
var result = File(pdf, MediaTypeNames.Application.Pdf, fileName);
if (sendToClientEmail) await _clientAppointmentDocumentService.SendToClientByEmailAsync(new[] {result});
if (!uploadToClientPortal) return result;
var accessToken = Request.Headers["Authorization"].ToString();
var response = await _clientAppointmentDocumentService.UploadToClientPortalAsync(documentId, result, accessToken);
return Ok(response);
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
}
This works well on all machines, although on Linux server there are images not included in resulting PDF under tag <img src="https://..." />.
What I have checked:
This is not SSL problem because if I make this controller to output HTML then images are shown as expected
I have tried every setting (maybe not in all combinations) in conversionSettings without any success
Converting images into base64 string also didn't help. Images didn't shown
Anybody have any ideas, what can I check further?
Had a similar issue on an application hosted on a Linux Ubuntu server and converting image to base 64 worked for me. Put the image in a folder called Resources and used the code below:
byte[] imageArray = File.ReadAllBytes(Path.Combine(Directory.GetCurrentDirectory(), "Resources", "logo.png"));
string base64ImageRepresentation = Convert.ToBase64String(imageArray);
Then the image in the html string:
string.Format("<img src=""data:image/png;base64,{0}"" width=""300"" height=""40"" />", base64ImageRepresentation);
Also had LoadImages set to true on the ObjectSettings:
var objectSettings = new ObjectSettings
{
WebSettings = { DefaultEncoding = "utf-8", LoadImages = true },
};
var pdf = new HtmlToPdfDocument()
{
Objects = { objectSettings }
};
Hope this helps!
Related
I'm trying to access a SharePoint instance using SharePoint Online Client Components SDK (sharepointclientcomponents_16-6518-1200_x64-en-us.msi) through .Net 6 console application.
My Code:
internal class SpMain
{
public static void GetApprovedAppsByView(
string siteUrl, NetworkCredential credentials, string listName, string viewName, string camlQuery)
{
try
{
using var context = new ClientContext(siteUrl);
context.Credentials = credentials;
var list = context.Web.Lists.GetByTitle(listName);
var view = list.Views.GetByTitle(viewName);
var query = new CamlQuery();
context.Load(view);
if (context.HasPendingRequest)
{
context.ExecuteQuery();
}
query.ViewXml = $"<View><Query>{view.ViewQuery}{camlQuery}</Query></View>";
var items = list.GetItems(query);
context.Load(items);
if (context.HasPendingRequest)
{
context.ExecuteQuery();
}
foreach (var listItem in items)
{
Console.WriteLine(listItem["Title"]);
}
}
catch (Exception ex)
{
Console.WriteLine("Error Message: " + ex.Message);
}
}
}
I'm calling this function like:
// Target Page: https://dummy.company.com/commy/cotrev/Lists/DevCMRL/DaMemos.aspx
const string siteUrl = "https://dummy.company.com/commy/cotrev/";
const string listName = "DevCMRL";
const string viewName = "DaMemos";
const string query = "*";
var credentials = new NetworkCredential("sp_testUser", "AwesomePassword", "myDomain");
SpMain.GetApprovedAppsByView(siteUrl, credentials, listName, viewName, query);
Console.ReadKey();
But I keep getting Bad Request at the first context.ExecuteQuery();. The credentials are correct. I think I have the Url, List, & View names set up incorrectly. And also, CamlQuery is completely wrong for sure. But this is something new for me and the docs at Microsoft are talking about SharePoint Online only.
Given the Target URL of the page with the actual List, how should I set the correct parameters?
I am trying to change the Display Image while playing mp3 file using CrossMediaManager, but It seems it doesn't change the image. It does change the title, id and so on, but I don't know why it can't change the display image.
async Task ExecutePlayChapterCommand()
{
if (IsBusy) return;
IsBusy = true;
try
{
await Task.Delay(TimeSpan.FromMilliseconds(10));
var reciter = Reciters.FirstOrDefault();
Device.BeginInvokeOnMainThread(async() => {
var mediaItem = await CrossMediaManager.Current.Play(reciter.DownloadURL);
CrossMediaManager.Current.Queue.Current.IsMetadataExtracted = false;
mediaItem.Title = reciter.Name;
var image = new Image() { Source = ImageSource.FromUri(new Uri(reciter.ImagePath)) };
CrossMediaManager.Current.Queue.Current.Title = string.Format("{0} - {1} ({2})", "1", "ChName", "English");
CrossMediaManager.Current.Queue.Current.Album = reciter.Name;
CrossMediaManager.Current.Queue.Current.Artist = reciter.Name;
CrossMediaManager.Current.Queue.Current.AlbumImage = image;
CrossMediaManager.Current.Queue.Current.AlbumImageUri = reciter.ImagePath;
CrossMediaManager.Current.Queue.Current.DisplayImage = image;
CrossMediaManager.Current.Queue.Current.DisplayImageUri = reciter.ImagePath;
CrossMediaManager.Current.Queue.Current.Image = image;
CrossMediaManager.Current.Queue.Current.ImageUri = reciter.ImagePath;
CrossMediaManager.Current.Notification.UpdateNotification();
});
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
finally { IsBusy = false; }
}
}
A sample project is uploaded in here
This answer might be a little bit late, but it worked for me perfectly.
First, the "Image, DisplayImage" properties of the media items are objects, that contain native representations of the cover image (UIImage and Bitmap) in the platform specific projects, so do not assign them any value.
Create your media item like this, without assigning the properties mentioned above.
new MediaItem(chapter.MediaUrl)
{
ImageUri = item.ImageUrl,
DisplayImageUri = item.ImageUrl,
AlbumImageUri = item.ImageUrl,
DisplayTitle = item.Title,
Title = item.Title,
Artist = item.Author,
Author = item.Author,
};
Then, on iOS, after setting the Queue items of the media items, in the platform specific project, create a method that takes the media item. This method will be called from the shared project.
Here is the method.
public void SetMediaImages(IMediaManager mediaManager)
{
if (mediaManager.Queue != null && mediaManager.Queue.Any())
{
foreach (var mediaItem in mediaManager.Queue)
{
if (!string.IsNullOrEmpty(mediaItem.ImageUri))
{
mediaItem.Image = UIImage.LoadFromData(NSData.FromUrl(new NSUrl(mediaItem.ImageUri)));
mediaItem.AlbumImage = mediaItem.Image;
}
}
}
}
Call this method in your shared project using Xamarin.Form's dependency service.
I want to retrieve image which i have it's path in phone
filepath = "/storage/emulated/0/Android/data/com.CommunicatorEye/files/Pictures/EmployeesCards/IMG_20190131_143513.jpg";
var image = DependencyService.Get<IDependency().RetriveImageFromLocation(filepath);
IDependency.cs
public interface IDependency
{
Task<Image> RetriveImageFromLocation(string location);
}
Android
DependencyImplementation.cs
public async Task<Image> RetriveImageFromLocation(string location)
{
Image image = new Image();
var memoryStream = new MemoryStream();
using (var source = System.IO.File.OpenRead(location))
{
await source.CopyToAsync(memoryStream);
}
image.Source = ImageSource.FromStream(() => memoryStream);
return image;
}
but it doesn't work for me , any sample ?
If that file is within your app's sandbox, there is no reason to use DI/DependencyService/etc... to obtain a stream to populate an ImageSource and then add that to an Image.
Use an FileImageSource (static ImageSource.FromFile) and supply it the path:
var image = new Image
{
Source = ImageSource.FromFile(filePath)
};
This is how you can get path of resources. keys should be declared in App.xaml file
public static String GetImagePath(string AppResourceName)
{
return (Application.Current.Resources[AppResourceName] as FileImageSource).File;
}
public static Color GetColor(string AppResourceName)
{
return (Color)Application.Current.Resources[AppResourceName];
}
I am facing one error sometimes, when I try get a picture from user gallery.
I get the picture in a Stream object, convert it to a base64 and I send it using a post function.
Sometimes The program says that the image path is big and, because of that, its not possible send the url.
I heard that its cause the image size but I am not sure about it...
Does someone know what really causes a Big base64 string? How can I solve that?
You could follow something similar to this:
http://jamessdixon.wordpress.com/2013/10/01/handling-images-in-webapi/
Maybe you need make some change in your web api like this:
[HttpPost]
public HttpResponseMessage UploadImage(int ID)
{
var result = new HttpResponseMessage(HttpStatusCode.OK);
if (Request.Content.IsMimeMultipartContent())
{
Request.Content.LoadIntoBufferAsync().Wait();
Request.Content.ReadAsMultipartAsync(new MultipartMemoryStreamProvider()).ContinueWith((task) =>
{
MultipartMemoryStreamProvider provider = task.Result;
foreach (HttpContent content in provider.Contents)
{
Stream stream = content.ReadAsStreamAsync().Result;
Image image = Image.FromStream(stream);
var testName = content.Headers.ContentDisposition.Name;
String filePath = HostingEnvironment.MapPath("~/Content/");
String fullPath = Path.Combine(filePath, ID.ToString()+".jpg");
image.Save(fullPath);
}
});
return result;
}
else
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "This request is not properly formatted"));
}
}
After making sure your api web works well you should change from Stream to byte[]
In your Xamarin code try this:
public async Task PostItem(String Controller, String Method, int ID, byte[] item)
{
using (var client = CreateClient ()) {
var da = new ByteArrayContent(item);
try{
var multi = new MultipartContent();
multi.Add(da);
var response = await client.PostAsync (Controller + "/" +Method + "/?ID=" +ID, multi);
}catch(Exception ex)
{
Console.Write(ex.Message);
}
}
}
For more infomation see MultipartContent class documentation
I am trying to download a zip file from a dotnet core web api action, but I can't make it work. I tried calling the action via POSTMAN and my Aurelia Http Fetch Client.
I am able to create the ZipFile like I want it and store it on the system, but can't fix it so it returns the zipfile via the api.
Use-case: User selects a couple of picture collections and clicks the download button. The ids of the picture collections gets send to the api and a zipfile is created which contains a directory for every picture collection which holds the pictures. That zipfile is returned to the user so he/she can store it on their system.
Any help would be appreciated.
My controller action
/// <summary>
/// Downloads a collection of picture collections and their pictures
/// </summary>
/// <param name="ids">The ids of the collections to download</param>
/// <returns></returns>
[HttpPost("download")]
[ProducesResponseType(typeof(void), (int) HttpStatusCode.OK)]
public async Task<IActionResult> Download([FromBody] IEnumerable<int> ids)
{
// Create new zipfile
var zipFile = $"{_ApiSettings.Pictures.AbsolutePath}/collections_download_{Guid.NewGuid().ToString("N").Substring(0,5)}.zip";
using (var repo = new PictureCollectionsRepository())
using (var picturesRepo = new PicturesRepository())
using (var archive = ZipFile.Open(zipFile, ZipArchiveMode.Create))
{
foreach (var id in ids)
{
// Fetch collection and pictures
var collection = await repo.Get(id);
var pictures = await picturesRepo
.GetAll()
.Where(x => x.CollectionId == collection.Id)
.ToListAsync();
// Create collection directory IMPORTANT: the trailing slash
var directory = $"{collection.Number}_{collection.Name}_{collection.Date:yyyy-MM-dd}/";
archive.CreateEntry(directory);
// Add the pictures to the current collection directory
pictures.ForEach(x => archive.CreateEntryFromFile(x.FilePath, $"{directory}/{x.FileName}"));
}
}
// What to do here so it returns the just created zip file?
}
}
My aurelia fetch client function:
/**
* Downloads all pictures from the picture collections in the ids array
* #params ids The ids of the picture collections to download
*/
download(ids: Array<number>): Promise<any> {
return this.http.fetch(AppConfiguration.baseUrl + this.controller + 'download', {
method: 'POST',
body: json(ids)
})
}
What I've tried
Note that what I've tried does not generate errors, it just doesn't seems to do anything.
1) Creating my own FileResult (like I used to do with older ASP.NET). Can't see the headers being used at all when I call it via postman or the application.
return new FileResult(zipFile, Path.GetFileName(zipFile), "application/zip");
public class FileResult : IActionResult
{
private readonly string _filePath;
private readonly string _contentType;
private readonly string _fileName;
public FileResult(string filePath, string fileName = "", string contentType = null)
{
if (filePath == null) throw new ArgumentNullException(nameof(filePath));
_filePath = filePath;
_contentType = contentType;
_fileName = fileName;
}
public Task ExecuteResultAsync(ActionContext context)
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(System.IO.File.ReadAllBytes(_filePath))
};
if (!string.IsNullOrEmpty(_fileName))
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = _fileName
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue(_contentType);
return Task.FromResult(response);
}
}
}
2) https://stackoverflow.com/a/34857134/2477872
Does nothing.
HttpContext.Response.ContentType = "application/zip";
var result = new FileContentResult(System.IO.File.ReadAllBytes(zipFile), "application/zip")
{
FileDownloadName = Path.GetFileName(zipFile)
};
return result;
I've tried it with a test dummy PDF file and that seemed to work with POSTMAN. But when I try to change it to the zipfile (see above) it does nothing.
HttpContext.Response.ContentType = "application/pdf";
var result = new FileContentResult(System.IO.File.ReadAllBytes("THE PATH/test.pdf"), "application/pdf")
{
FileDownloadName = "test.pdf"
};
return result;
To put a long story short, the example below illustrates how to easily serve both a PDF as well as a ZIP through a dotnet-core api:
/// <summary>
/// Serves a file as PDF.
/// </summary>
[HttpGet, Route("{filename}/pdf", Name = "GetPdfFile")]
public IActionResult GetPdfFile(string filename)
{
const string contentType = "application/pdf";
HttpContext.Response.ContentType = contentType;
var result = new FileContentResult(System.IO.File.ReadAllBytes(#"{path_to_files}\file.pdf"), contentType)
{
FileDownloadName = $"{filename}.pdf"
};
return result;
}
/// <summary>
/// Serves a file as ZIP.
/// </summary>
[HttpGet, Route("{filename}/zip", Name = "GetZipFile")]
public IActionResult GetZipFile(string filename)
{
const string contentType ="application/zip";
HttpContext.Response.ContentType = contentType;
var result = new FileContentResult(System.IO.File.ReadAllBytes(#"{path_to_files}\file.zip"), contentType)
{
FileDownloadName = $"{filename}.zip"
};
return result;
}
This sample just works™
Notice in this case there is only one main difference between the two actions (besied the source file name, of course): the contentType that is returned.
The example above uses 'application/zip', as you've mentioned yourself, but it might just be required to serve a different mimetype (like 'application/octet*').
This leads to the speculation that either the zipfile cannot be read properly or that your webserver configuration might not be configured properly for serving .zip files.
The latter may differ based on whether you're running IIS Express, IIS, kestrel etc. But to put this to the test, you could try adding a zipfile to your ~/wwwroot folder, making sure you have enabled serving static files in your Status.cs, to see if you can download the file directly.