I have been trying to upload multiple pictures to a SQL Server database by using Entity Framework.
I have successfully uploaded one picture to the database before but I need to upload multiple pictures one time.
I tried to take the pictures as ICollection<IFormFile> Pic
in the DtoModel and convert them to byte[] in the primary model to store them in the database but it didn't work and I just stored one picture in the database from many I have uploaded.
primary model:
public class property
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
[Required(ErrorMessage = "Photo is required.")]
public byte[] Pic { get; set; }
public string PicFromat { get; set; }
}
DtoModel:
public class dtoprop
{
public string Name { get; set; }
public ICollection<IFormFile> Pic { get; set; }
public string PicFromat { get; set; }
}
Controller:
private readonly ApplicationDbContext _context;
private new List<string> _allowedExtenstions = new List<string> { ".jpg", ".png" };
private long _maxAllowedPosterSize = 1048576;
public propertiesController(ApplicationDbContext context)
{
_context = context;
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create( dtoprop model)
{
if (!ModelState.IsValid)
{
return View( model);
}
if(!_allowedExtenstions.Contains(Path.GetExtension(model.Pics.FileName.ToLower())))
{
ModelState.AddModelError("Pic", "Only .PNG, .JPG images are allowed!");
return View(model);
}
if (model.Pics.Length > _maxAllowedPosterSize)
{
ModelState.AddModelError("Pic", "Poster cannot be more than 1 MB!");
return View(model);
}
using var dataStream = new MemoryStream();
await model.Pics.CopyToAsync(dataStream);
using var dataStream = new MemoryStream();
byte[] conv = null;
if (model.Pic != null )
{
// Loop thru each selected file
foreach (IFormFile photo in model.Pic)
{
await photo.CopyToAsync(dataStream);
conv = dataStream.ToArray();
}
}
var pic = new property
{
Name = model.Name,
Pic = conv,
PicFromat = model.PicFromat
};
_context.Properties.Add(pic);
_context.SaveChanges();
return RedirectToAction("Index");
}
So, do I need to create a new image model with a one-to-many relationship with the primary model? or there is a better way can implement this approach without store the images in the wwwroot folder?
CodePudding user response:
rewrite your controller code block as follows:
if (model.Pic != null )
{
// Loop thru each selected file
foreach (IFormFile photo in model.Pic)
{
var dataStream = new MemoryStream();
await photo.CopyToAsync(dataStream);
byte[] conv = dataStream.ToArray();
var pic = new property()
{
Name = model.Name,
Pic = conv,
PicFromat = model.PicFromat
};
_context.Properties.Add(pic);
}
}
_context.SaveChanges();
CodePudding user response:
Here are few things you need to consider for such approach i.e.
Make sure that the underlying database e.g. in case of SQL server must have data type as VARBINARY(MAX). this will store the byte array data in a single row.
Seeing your code I can tell that you actually needs multi-row List<byte[]> data type to store the multiple images bytes inside a single row of table.
Since you are just using the datatype byte[] it means that in your foreach loop the last image you convert to byte array will be stored in your variable, meaning in your conv variable you are storing the bytes of single image not multiple images.
Know that any database infrastructure does not allows to store list of anything even byte array in a single row, you eventually need to use one to may database relation to store the list of multiple files byte array in a multiple rows separately.
You may have found some hack, but it eventually runs to data type max limits as either your number of images increases or your image size increases.
In short, you need one to many relation mapping model to store multiple images even in bytes in the database, you can not store multiple images byte array in a single row by design. I hope I am able to clarify few things here.