-
Notifications
You must be signed in to change notification settings - Fork 27
Handling Images and Files with property Transformers
images and files can be added to your ViewModels by using IFormFile
. property transformers are attributes which can be used to modify how data is stored and indexed. it's best to explain with an example so let's look at how images are handled in Puck by default.
let's look at a simple ViewModel with an image:
public class Page:BaseModel
{
[UIHint("PuckImage")]
public PuckImage Image { get; set; }
}
the Page
ViewModel above has a property of type PuckImage
, which is a model which comes with Puck. let's look at PuckImage
to see its properties.
[PuckImageTransformer()]
public class PuckImage
{
public string Path { get; set; }
public string Description { get; set; }
public string Size {get;set;}
public string Extension { get; set; }
public int? Width { get; set; }
public int? Height { get; set; }
public List<CropModel> Crops { get; set; }
public IFormFile File { get; set; }
}
as you can see above, PuckImage
is a pretty straightforward class with mostly properties for image metadata such as Width
and Height
. Notice there is also a IFormFile
property for file upload and also notice that there is a Transformer attribute above the class, [PuckImageTransformer()]
. this attribute will process the image when the ViewModel is saved and extract the metadata from the uploaded file.
let's take a look at the source code for PuckImageTransformer
:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)]
public class PuckImageTransformer : Attribute, I_Property_Transformer<PuckImage, PuckImage>
{
public async Task<PuckImage> Transform(BaseModel m,string propertyName,string ukey,PuckImage p)
{
try
{
if (p.File == null || string.IsNullOrEmpty(p.File.FileName))
return null;
string filepath = string.Concat("~/wwwroot/Media/", m.Id, "/", m.Variant, "/", ukey, "_", p.File.FileName);
string absfilepath =ApiHelper.MapPath(filepath);
new FileInfo(absfilepath).Directory.Create();
using (var stream = new FileStream(absfilepath, FileMode.Create)) {
p.File.CopyTo(stream);
}
p.Path = filepath.Replace("~/wwwroot","");
p.Size = p.File.Length.ToString();
p.Extension=Path.GetExtension(p.File.FileName);
var img = Image.Load(absfilepath);
p.Width = img.Width;
p.Height = img.Height;
}catch(Exception ex){
}finally {
p.File = null;
}
return p;
}
}
it's mostly straightforward. there's a Transform method where all the action happens but first let's look at the interface PuckImageTransformer
implements - I_Property_Transformer<T,T>
. the first Type parameter is for the final argument of the Transform method and the second Type parameter is for the return type of the Transform method. in this instance, both Type parameters are of PuckImage
, meaning that the Transform method accepts a PuckImage
argument and returns a PuckImage
too.
now let's look at all the arguments sent to the Transform method: BaseModel m,string propertyName,string ukey,PuckImage p
. the first argument is the current ViewModel being saved cast as a BaseModel
, the string propertyName
is the name of the property being transformed and the ukey
is the unique key which is just the property name including array index if the property is inside an array - for example for a property inside an array the propertyName
might be Names where the ukey
might be Names[0]. the final argument is the property being transformed which in this case, is a PuckImage
.
if you look at the body of the Transform method, it's just saving the image to a directory and setting the Path
to the saved file and setting other metadata.
this is necessary and an error will likely be thrown if you don't set the file property to null once you're done processing. you can see the PuckImageTransformer
doing this in the finally{}
block.
there is a second image transformer you can use with PuckImage
and that's the [PuckAzureBlobImageTransformer()]
. this transformer handles images by saving them to Azure blob storage and saving a path to the file. to use this transformer, you need to set the AzureImageTransformer_AccountName
and AzureImageTransformer_AccessKey
AppSettings in the appSettings.json with your Azure storage account credentials.
rather than adding an image directly to your ViewModel, you can have a dedicated Image ViewModel. by default there's one in the ViewModels
folder of the PuckWeb
project, called ImageVM
.
this way, you can save an image as a dedicated piece of content in your site content tree, maybe in an "Images" folder and then link it to other content items using the PuckImagePicker
content selector.
you can then re-use the same image by linking it to multiple pieces of content.