Whys is .Save() creating duplicate files? - c#

My project takes an uploaded file from the user, as well as some data points from JCrop, and crops the image to the selected portion. Then, the cropped image is given a new name, and saved to the server. However, the image is saved to the server twice, once with the new name, and once again with a completely different name.
CONTROLLER
[HttpPost]
[ValidateAntiForgeryToken]
[ValidImageFile]
public ActionResult _Image(ImageViewModel VM)
{
if (ModelState.IsValid)
{
//Convert from HttpPostedBase to WebImage, then crop the image
byte[] data;
using (Stream inputStream = VM.uploadFile.InputStream)
{
MemoryStream memoryStream = inputStream as MemoryStream;
if (memoryStream == null)
{
memoryStream = new MemoryStream();
inputStream.CopyTo(memoryStream);
}
data = memoryStream.ToArray();
}
var Image = new WebImage(data);
int T = VM.y1;
int L = VM.x1;
int B = Image.Height - VM.y2;
int R = Image.Width - VM.x2;
Image.Crop(T, L, B, R);
//if the user already has an image for their character, delete it.
string oldImage = HttpContext.Server.MapPath(db.Characters.Find(VM.Id).imagePath);
if (System.IO.File.Exists(oldImage))
{
System.IO.File.Delete(oldImage);
}
//generate random file name path, save file to server.
var fileName = Guid.NewGuid().ToString();
var filePath = Path.Combine("/Content/Images/charPhoto/", fileName);
var uploadUrl = HttpContext.Server.MapPath(filePath);
Image.Save(uploadUrl);
//save file path to db.
filePath = filePath + "." + Image.ImageFormat;
db.Characters.Find(VM.Id).imagePath = filePath;
db.SaveChanges();
//returns the image path to jQuery.
return Content(filePath);
}
return View();
}
VIEWMODEL
public class ImageViewModel
{
public Guid? Id { get; set; }
public string ImagePath { get; set; }
public HttpPostedFileBase uploadFile { get; set; }
[Required]
public int x1 { get; set; }
[Required]
public int y1 { get; set; }
[Required]
public int x2 { get; set; }
[Required]
public int y2 { get; set; }
}
proof of duplication :
Can anybody explain to me why this happens? Is this some intentional portion of WebImage for data backup purposes? If not, what have I done wrong, and what should I do instead?

OMG
string oldImage = db.Characters.Find(VM.Id).imagePath;
if (System.IO.File.Exists(HttpContext.Server.MapPath(oldImage)))
{
System.IO.File.Delete(HttpContext.Server.MapPath(oldImage));
}
var filePath = Path.Combine("/Content/Images/charPhoto/", Guid.NewGuid().ToString() + "." + Image.ImageFormat);
Image.Save(HttpContext.Server.MapPath(filePath));
//save file path to db.
db.Characters.Find(VM.Id).imagePath = filePath;
db.SaveChanges();
//returns the image path to jQuery.
return Content(filePath);

Related

How to download multiple files in ASP.NET Core MVC

How to download multiple files? If the user sends a single file, display it, and if the user sends multiple files, how to set?
This is my controller code:
public IActionResult DownloadFile(int id)
{
byte[] bytes;
string fileName, contentType;
var model = new List<Files>();
var getdocument = _documentdata.GetDocumentbyDocumentId(id);
if (getdocument != null)
{
fileName = getdocument.Name;
contentType = getdocument.FileType;
bytes = getdocument.DataFiles;
return File(bytes, contentType: "application/octet-stream", fileName);
}
return Ok("Can't find the image");
}
Upload is working storing in database 2 tables when I click uploaded download ?
Getting document single id
public Files GetDocumentbyDocumentId(int documentId)
{
var list = (from doc in _auc.Files
where doc.DocumentId == documentId
select doc).FirstOrDefault();
return list;
}
To download multiple file, you can create a zip file and add the multiple files inside it. Refer to the following sample:
In this sample, I will query the Products table and get their image content, then download them using a Zip file.
public IActionResult DownLoadAll()
{
var zipName = $"TestFiles-{DateTime.Now.ToString("yyyy_MM_dd-HH_mm_ss")}.zip";
using (MemoryStream ms = new MemoryStream())
{
//required: using System.IO.Compression;
using (var zip = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
//QUery the Products table and get all image content
_dbcontext.Products.ToList().ForEach(file =>
{
var entry = zip.CreateEntry(file.ProImageName);
using (var fileStream = new MemoryStream(file.ProImageContent))
using (var entryStream = entry.Open())
{
fileStream.CopyTo(entryStream);
}
});
}
return File( ms.ToArray(), "application/zip", zipName);
}
}
Code in the Index page:
<a asp-controller="Product" asp-action="DownLoadAll">DownLoad All Images</a>
Products model:
public class Product
{
[Key]
public int ProId { get; set; }
public string ProName { get; set; }
public string ProImageName { get; set; }
public byte[] ProImageContent { get; set; } //Image content.
public string ProImageContentType { get; set; }
//you can add another properties
}
The output as below:

Checking if data exist in sqlite-net before inserting them to database

Im making an Xamarin.Android application that will use "templates" , template is this:
[Table("Templates")]
public class Template
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public int Category { get; set; }
//[TextBlob("imagesBlobbed")]
[OneToMany, Unique]
public List<TemplateImage> TemplateImages { get; set; }
public string ImagesHash { get; set; }
//public string imagesBlobbed { get; set; }
}
[Table("TemplateImages")]
public class TemplateImage
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public int Category { get; set; }
public string ImagesHash { get; set; }
public int Image { get; set; }
[ForeignKey(typeof(Template))]
public int TemplateId { get; set; }
}
i want all the TemplateImages objects to be unique in my database, the attribute Unique doesnt do anything, cause i guess because TemplateImage table has an auto increment Id will always be unique, no matter if the Image or ImageHash is the same in 2 records.
I was thinking then how else i can be sure that TemplateImages will be unique (Notice: when i say unique i mean that there isn't any other List that have the exact Image for every TemplateImage, in the same order).
Also i was using the ResourceID of the images, as image, which is wrong cause they might change on every new compiled app update.
So i decided to make a hash md5 from the images. They only way that i have find to load resource images (my images are vector .xml files) in Xamarin.Android is by converting the resource to bitmap, and then convert the bitmap to byte, and then the byte to md5.
And also i should have a hash string for the template item too. so im creating an md5 hash for the template by combining all the byte[] of the images into one and then hasing that.
I create this crazy code for this job:
public static string GetMD5Hash(byte[] content)
{
using (var md5 = MD5.Create())
{
byte[] computedHash = md5.ComputeHash(Encoding.UTF8.GetBytes(BitConverter.ToString(content).Replace("-", "")));
return new System.Runtime.Remoting.Metadata.W3cXsd2001.SoapHexBinary(computedHash).ToString();
}
}
private static SQLiteConnection instance;
public static SQLiteConnection db()
{
if (instance == null)
instance = new SQLiteConnection(System.IO.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "TemplatesData.db"));
return instance;
}
public static void CloseConnection()
{
if (instance != null)
{
instance.Close();
instance.Dispose();
instance = null;
}
}
}
public class TemplateDB
{
public static byte[] ConcatByteList(List<byte[]> list)
{
return list
.SelectMany(a => a)
.ToArray();
}
public static Bitmap GetBitmapFromVectorDrawable(Context context, int drawableId)
{
Drawable drawable = ContextCompat.GetDrawable(context, drawableId);
if (Build.VERSION.SdkInt < Android.OS.BuildVersionCodes.Lollipop)
{
drawable = (DrawableCompat.Wrap(drawable)).Mutate();
}
Bitmap bitmap = Bitmap.CreateBitmap(drawable.IntrinsicWidth,
drawable.IntrinsicWidth, Bitmap.Config.Argb8888);
Canvas canvas = new Canvas(bitmap);
drawable.SetBounds(0, 0, canvas.Width, canvas.Height);
drawable.Draw(canvas);
return bitmap;
}
public byte[] DrawableToByteArray(int resourceId)
{
var context = AppState.ApplicationState;
using (var bitmap = GetBitmapFromVectorDrawable(context, resourceId))
{
int size = bitmap.ByteCount;
byte[] byteArray = new byte[size];
ByteBuffer byteBuffer = ByteBuffer.Allocate(size);
bitmap.CopyPixelsToBuffer(byteBuffer);
byteBuffer.Rewind();
byteBuffer.Get(byteArray);
return byteArray;
}
}
public static void AddTemplate(int category, List<int> images)
{
var templateDB = new TemplateDB();
var imageByteList = new List<byte[]>();
foreach (int image in images)
{
imageByteList.Add(templateDB.DrawableToByteArray(image));
}
var tmpl = new Template()
{
Category = category,
};
var img1 = new TemplateImage()
{
Category = category,
Image = images[0],
ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[0]),
};
var img2 = new TemplateImage()
{
Category = category,
Image = images[1],
ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[1]),
};
var img3 = new TemplateImage()
{
Category = category,
Image = images[2],
ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[2]),
};
var img4 = new TemplateImage()
{
Category = category,
Image = images[3],
ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[3]),
};
var img5 = new TemplateImage()
{
Category = category,
Image = images[4],
ImagesHash = DatabaseHelper.GetMD5Hash(imageByteList[4]),
};
tmpl.TemplateImages = new List<TemplateImage>() { img1, img2, img3, img4, img5 };
tmpl.ImagesHash = DatabaseHelper.GetMD5Hash(ConcatByteList(imageByteList));
var result = DatabaseHelper.db().Query<TemplateImage>("Select * from Templates where ImagesHash=?", tmpl.ImagesHash);
if (result.Count == 0)
{
DatabaseHelper.db().InsertAll(tmpl.TemplateImages);
DatabaseHelper.db().Insert(tmpl);
DatabaseHelper.db().UpdateWithChildren(tmpl);
}
}
Which all of a sudden, gives out of memory exception.
After thinking for a while that i should stop programming and start belly dancing, i think that since im giving in sqlite AddTemplate function a list of ResourceIds that i have to manually create (can't avoid it), then why not to give them my own string hash code and compare that to find if a record exist?
What is the correct approach?
In the end, i was thinking to use guid as extra id fields and add the parameter unique,in that case i could be sure which templates have been inserted, but i decided to use transactions. With transactions i can know that all my data is created or none and also save the creation into a variable in SharedPreferences and avoid recreating or even checking if the database exist.
For checking if the data is correctly updated and no recreation is needed i used this code:
public bool FirstRun { get; set; } = true;
public int DatabaseCreatedVersionOf { get; set; } = -1;
public override void OnCreate()
{
base.OnCreate();
}
public void UpdateDatabaseCreatedVersion()
{
DatabaseCreatedVersionOf = Preferences.Get(Settings.DatabaseCreatedVersionOfKey,
Settings.DatabaseCreatedVersionOfDefault);
}
public void CreateTemplateDB()
{
UpdateDatabaseCreatedVersion();
if (DatabaseCreatedVersionOf == -1)
TemplateDB.CreateDB();
FirstRun = false;
}
public Template GetTemplateById(int id)
{
if (FirstRun)
{
CreateTemplateDB();
FirstRun = false;
}
return TemplateDB.GetTemplate(id);
}
public List<Template> GetAllTemplates()
{
if (FirstRun)
{
CreateTemplateDB();
FirstRun = false;
}
return TemplateDB.GetAllTemplates();
}
Now all i have to do is call GetTemplateById or GetAllTemplates and if any creation is required, it will happen.

ASP.NET Web API upload image to SQL Server database

I'm writing an article API with image and I've been following this tutorial about uploading file in ASP.NET web API, the Title and Content are saved to database as intended.
The problem here is, the images that I post are saved to my local folder but the fields Filename, Filepath, Filelength, and Filecreatedtime aren't saved to database.
Posting article to database with postman:
The ImageFolder:
GET index:
The database:
here's my Article model:
namespace Wwf_Article.Models
{
using System;
using System.Collections.Generic;
public partial class Article
{
public int ID { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public string FileName { get; set; }
public string FilePath { get; set; }
public float FileLength { get; set; }
public DateTime FileCreatedTime { get;
}
}
and here's my POST controller
[Mime]
public async Task<IHttpActionResult> Post()
{
var fileuploadPath = HttpContext.Current.Server.MapPath("~/ImageFolder");
var multiFormDataStreamProvider = new MultiFileUploadProvider(fileuploadPath);
await Request.Content.ReadAsMultipartAsync(multiFormDataStreamProvider);
string uploadingFileName = multiFormDataStreamProvider.FileData.Select(x => x.LocalFileName).FirstOrDefault();
Article article = new Article
{
Title = HttpContext.Current.Request.Form["Title"],
Content = HttpContext.Current.Request.Form["Content"],
//these four lines below aren't saved to DB
FilePath = uploadingFileName,
FileName = Path.GetFileName(uploadingFileName),
FileLength = new FileInfo(uploadingFileName).Length,
FileCreatedTime = DateTime.Now
};
db.Articles.Add(article);
db.SaveChanges();
return Ok();
}
Any idea how to fix this problem?
Are the 4 four lines below as you mentionned part of your Article model class?
I mean (FilePath, FileName, FileLength and FileCreatedTime)
1- If yes, i think you can use a stored procedure to achieve your request like the following
CREATE PROC spInsertArticle
#Title varchar(100),
#Content varchar(100),
#FileName varchar(500),
#FilePath varchar(500),
#FileLength varchar(500)
AS
Insert Into ArticleTable(Title,Content,FileName,
FilePath,FileLength,FileContentTime)
Values (#Title,#Content,#FileName,
#FilePath,#FileLength,GetDate())
2-Go back to your api project and create a model class called ArticleModel:
public class ArticleModel
{
public string Title {get; set; }
public string Content {get; set; }
public string FileName {get; set; }
public string FilePath {get; set; }
public string FileLength {get; set; }
}
3-Create an api post method in the ArticleController
[Route("api/ArticleController/PostArticle")]
public HttpResponseMessage PostArticle(ArticleModel obj)
{
if (ModelState.IsValid)
{
try
{
string PhotoPath = Convert.ToString(ConfigurationManager.AppSettings["ImagePath"]);
ArticleModel newObj = new ArticleModel();
newObj.Title = obj.Title ;
newObj.Content = obj.Content;
newObj.FileName = obj.FileName;
newObj.FilePath = obj.FilePath;
newObjl.FileLength = obj.FileLength;
if (String.IsNullOrEmpty(newObj.Content))
{
}
else
{
string startingFilePath = PhotoPath;
string FilePath = SaveImage(newObj.Content, startingFilePath, newObj.FileName);
FileInfo fInfo = new FileInfo(FilePath);
newObj.Content = fInfo.Name;
}
ArticleEntities context = new ArticleEntities();
var newArticle = context.spInsertArticle(newObj.Title, newObj.Content,
newObj.FileName, newObj.FilePath, newObj.FileLength);
return Request.CreateResponse(HttpStatusCode.Created, newArticle);
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, ex);
}
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest, ModelState);
}
}
4-And finally create the SaveImage method mentionned in the PostArticle method
private string SaveImage(string base64, string FilePath, string ImageName)
{
//Get the file type to save in
var FilePathWithExtension = "";
string localBase64 = "";
if (base64.Contains("data:image/jpeg;base64,"))
{
FilePathWithExtension = FilePath + ImageName + ".jpg";
localBase64 = base64.Replace("data:image/jpeg;base64,", "");
}
else if (base64.Contains("data:image/png;base64,"))
{
FilePathWithExtension = FilePath + ImageName + ".png";
localBase64 = base64.Replace("data:image/png;base64,", "");
}
else if (base64.Contains("data:image/bmp;base64"))
{
FilePathWithExtension = FilePath + ImageName + ".bmp";
localBase64 = base64.Replace("data:image/bmp;base64", "");
}
else if (base64.Contains("data:application/msword;base64,"))
{
FilePathWithExtension = FilePath + ImageName + ".doc";
localBase64 = base64.Replace("data:application/msword;base64,", "");
}
else if (base64.Contains("data:application/vnd.openxmlformats-officedocument.wordprocessingml.document;base64,"))
{
FilePathWithExtension = FilePath + ImageName + ".docx";
localBase64 = base64.Replace("data:application/vnd.openxmlformats-officedocument.wordprocessingml.document;base64,", "");
}
else if (base64.Contains("data:application/pdf;base64,"))
{
FilePathWithExtension = FilePath + ImageName + ".pdf";
localBase64 = base64.Replace("data:application/pdf;base64,", "");
}
using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(localBase64)))
{
using (FileStream fs = new FileStream(FilePathWithExtension, FileMode.Create, FileAccess.Write))
{
//Create the specified directory if it does not exist
var photofolder = System.IO.Path.GetDirectoryName(FilePathWithExtension);
if (!Directory.Exists(photofolder))
{
Directory.CreateDirectory(photofolder);
}
ms.WriteTo(fs);
fs.Close();
ms.Close();
}
}
return FilePathWithExtension;
}
5-Try this in either Postman or swagger and it will work for you. I am available for any discussion
First of all, create a Generic interface repo class
public interface IRepository<T> where T : class
{
T GetById(int id);
void Add(T entity);
}
Create a class that will implement the interface
public class EFRepository<T> : IRepository<T> where T : class
{
public EFRepository(PayrollcshrnewEntities dbContext)
{
if (dbContext == null)
throw new ArgumentNullException("dbContext");
DbContext = dbContext;
DbSet = DbContext.Set<T>();
}
protected ArticleEntities DbContext { get; set; }
protected DbSet<T> DbSet { get; set; }
public virtual T GetById(int id)
{
return DbSet.Find(id);
}
public virtual void Add(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State != EntityState.Detached)
{
dbEntityEntry.State = EntityState.Added;
}
else
{
DbSet.Add(entity);
}
}
}
For some reason after I delete the edmx and recreate the EF designer table with ADO, the POST controller works.
It turns out there's missing fields in the edmx diagram.
Thanks for all the help anyway, guys

Create a csv file with file name and path specified

I know this should be simple but I am naive in c#. I have the following object to generate as csv. and I am successfully able to generate a csv string using the method here
[Serializable]
public class UserSession
{
public string UserName { get; set; }
public string UserGuid { get; set; }
public string MachineGuid { get; set; }
public Guid SessionGuid { get; set; }
public DateTime LoginTime { get; set; }
public DateTime? LogOffTime { get; set; }
public TimeSpan? DesktopReady { get; set; }
public bool IsReadable { get; set; }
public int SessionId { get; set; }
}
Now I want to save this file in a specified path (for eg :
c://csvfolder/{myFilename}.csv
Can anyone suggest a method to write this csv file to 'myFilename.csv' and save it in c://csvfolder in c#??
That should be pretty simple. It looks like you have a generic class that can serialize your object into a string buffer.
All there's left is to take the string containing the CSV data and write it to a file. Fortunately that's quite trivial in C#:
string path = #"c:\csvfolder\myFilename.csv";
// assuming there's a UserSession object called userSessionObject..
string csvData = ToCsv<UserSession>(",", new [] {userSessionObject});
File.WriteAllText(path, csvData);
Try the following code, for saving the CSV File (Includes the creation of CSV)
string path = "c://csvfolder";
string fileName = "FileName.csv";
StreamWriter SW = new StreamWriter(path, false);
SW.Write( /*Can create the comma sperated items*/);
/*Create y our CSV file data here*/
fs = File.Open(path, FileMode.Open);
int BUFFER_SIZE = Convert.ToInt32(fs.Length);
int nBytesRead = 0;
Byte[] Buffer = new Byte[BUFFER_SIZE];
nBytesRead = fs.Read(Buffer, 0, BUFFER_SIZE);
fs.Close();
Response.AddHeader("Content-disposition", "attachment; filename=" + fileName);
Response.ContentType = "application/octet-stream";
Response.BinaryWrite(Buffer);
Response.Flush();
Response.End();

WebImage helper not creating correct path to image?

My problem is with the following piece of code. I have built WebPages app where this chunk of code works perfectly, however, in my MVC5 app it copies only the local path from my PC to MSSQL database, without a GUID even. The piece of code is:
CONTROLLER:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include="productId,categoryId,brandId,Name,Price,ProductImg")] Product product)
{
if (ModelState.IsValid)
{
WebImage photo = null;
var newFileName = "";
var imagePath = "";
//RIJEŠITI NESTED IF
//zašto ne prihvaća HttpPostedFileBase tip??
photo = WebImage.GetImageFromRequest();
if (photo != null)
{
newFileName = Guid.NewGuid().ToString() + "_" +
Path.GetFileName(photo.FileName);
imagePath = #"Content\Images\" + newFileName;
photo.Save(#"~\" + imagePath);
product.ProductImg = #"~\" + imagePath;
}
try
{
db.Entry(product).State = System.Data.Entity.EntityState.Modified;
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
var objContext = ((IObjectContextAdapter)db).ObjectContext;
objContext.Refresh(System.Data.Entity.Core.Objects.RefreshMode.ClientWins, product);
}
return RedirectToAction("Index");
}
}
MODEL:
public class Product
{
[ScaffoldColumn(false)]
public int productId { get; set; }
[DisplayName("Category")]
public int categoryId { get; set; }
[DisplayName("Brand")]
public int brandId { get; set; }
[DisplayName("Product Name")]
[Required(ErrorMessage = "Product Name is mandatory")]
[StringLength(160)]
public string Name { get; set; }
[Required(ErrorMessage = "Price is required")]
[Range(0.01, 7000.00,
ErrorMessage = "Price must be between 0.01 and 7000.00")]
public decimal Price { get; set; }
[DisplayName("Product Image")]
[StringLength(1024)]
public string ProductImg { get; set; }
public virtual Category Category { get; set; }
public virtual Brand Brand { get; set; }
public virtual List<OrderDetail> OrderDetails { get; set; }
}
Also how do I prevent this code from writing NULL to database in case that the user doesn't want to change the image?
Don't trust Filename provided by browser: some browsers send the full path others send only the file name. So you'd vetter use the following code to upload your picture/file
//-----
String path = Server.MapPath("~/content/public");
if (Request.Files != null && Request.Files.Count > 0)
{
String fileExtension = System.IO.Path.GetExtension(Request.Files[0].FileName).ToLower();
List<string> allowedExtensions = new List<string>(){".gif", ".png", ".jpeg", ".jpg" };
if (allowedExtensions.Contains(fileExtension))
{
string fileName = Guid.NewGuid().ToString();
Request.Files[0].SaveAs(path + fileName);
product.ProductImg = fileName ;
}
}
///------
and to display this image, use a simple img tag as follows
#{string imageUrl=Url.Content("~/content/public/"+Model.ProductImg); }
<img src="#imageUrl" alt=""/>
This can provide you with a guid...or you'll need to remove / or \ as follows
string fileName = Guid.NewGuid().ToString();
fileName +="_"+Request.Files[0].FileName.Split(new char[]{'/','\'}).ToList().LastOrDefault();

Categories