Playframework 2.0.1 Model with an uploaded file reference - model-view-controller

I'm confused about how should I do a model that has some uploaded file, like for exemplo:
User has photos.
I already found out how to upload a file, but the question here is about what to do with the file that was now uploaded, how can I link this new uploaded file(photo in the exemple) with a model(the user in the example).
Thanks.
OBS: Using play for Java here, not Scala.

You have to link your User to his picture. For that, your best option is to use the User id, which should be unique.
Then, if you uploaded your photo under the pictures/user folder in your filesystem, then you should save the picture as pictures/user/USER_ID.png (png or jpg or anything else).
Then, you can have an action which retrieve the picture according to the user id:
public static Result picture(String userId) {
Picture picture = Picture.findPicture(userId);
if (picture != null) {
response().setContentType(picture.contentType);
return ok(picture.bytes);
}
return notFound();
}
And the Picture class looks like:
public class Picture {
public byte[] bytes;
public String contentType;
public static Picture findPicture(String userId) {
String[] extensions = {"png","jpg"}; // an enum should be better
for (String extension:extensions) {
String path = "pictures/user/" + userId + "." + extension;
if (new File().exists(path)) {
Picture picture = new Picture();
picture.bytes = IOUtils.toByteArray(new FileInpustream(path));
picture.contentType = findContentType(extension);
return picture;
}
}
return null;
}
protected static String findContentType(String extension) {
if (extension.equalsIgnoreCase("jpg") {
return "image/jpeg";
} else if (extension.equalsIgnoreCase("png") {
return "image/png";
}
}
}
I did something similar once (but the pictures were stored in memory), you can take a look here.

Just create a convention if user has only one picture. Per instance, if your user was registered in 2012-07-23 and has id = 100, move the file to some place mapped from these data:
/uploaded-dir/2012/07/23/100/picture.jpg
After that, you can use the same convention to read the file.

Related

There is no argument given that corresponds to the required formal parameter 'photo' of 'Interface.Create(Trademark, IFormFile)'?

I am using .Net Core 5 and uploading images for my Trademark. I use Repository for my work and got error CS706: There is no argument given that corresponds to the required formal parameter 'photo' of 'Interface.Create(Trademark, IFormFile)' in Controller
_trademarkRepo.CreateNewTrademark(trademark);
Controller
public IActionResult CreateTrademark(Trademark trademark)
{
if(ModelState.IsValid)
{
_trademarkRepo.CreateNewTrademark(trademark);
}
_logger.LogInformation("...");
return RedirectToAction("Index");
}
Repo
public bool CreateNewTrademark(Trademark trademark, IFormFile photo)
{
var path = Path.Combine(this._webHostEnvironment.WebRootPath, "trademarks", photo.FileName);
var stream = new FileStream(path, FileMode.Create);
photo.CopyToAsync(stream);
if(CheckExist(trademark.TrademarkName))
{
return false;
}
var newTrademark = new Trademark
{
TrademarkName = trademark.TrademarkName,
Description = trademark.Description,
Image = photo.FileName
};
_dbContext.Trademarks.Add(newTrademark);
_dbContext.SaveChanges();
return true;
}
From error it is evident that what error is.
Method at repo level required two argument. One is trademark and another is photo.
When you have called that from controller , you have only passed one. (Trademark only and photo is missing). This is the error.
Basically your controller should look like following.
public IActionResult CreateTrademark(Trademark trademark,IFromFile photo)
{
if(ModelState.IsValid)
{
_trademarkRepo.CreateNewTrademark(trademark,photo);
}
_logger.LogInformation("...");
return RedirectToAction("Index");
}
Note: There are many other dependencies like how you post file from UI etc. That is not scope of this question and so answer. You have to look for those detail.

asp.net core how to delete existing image from a folder

this is my function to update an instance but here i can't delete the old image it is only delete the instance from the database but the real image doesn't deleted from the image folder i have got this error message
The instance of entity type 'Images' cannot be tracked because another instance with the same key value for {'ImageId'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
public async Task<IActionResult> Edit( int id, [Bind("ImageId,Title,ImageFile")] Images images )
{
if (id != images.ImageId)
{
return NotFound();
}
if (ModelState.IsValid)
{
var im = await _context.Images.FindAsync(id);
//delete image from wwwroot/image
var imagePath = Path.Combine(_hostEnvironment.WebRootPath, "image", im.ImageName);
if (System.IO.File.Exists(imagePath))
System.IO.File.Delete(imagePath);
//save image to wwwroot/image
string wwwRootPath = _hostEnvironment.WebRootPath;
string fileName = Path.GetFileNameWithoutExtension(images.ImageFile.FileName);
string extension = Path.GetExtension(images.ImageFile.FileName);
images.ImageName = fileName = fileName + DateTime.Now.ToString("yymmssfff") + extension;
string path = Path.Combine(wwwRootPath + "/Image", fileName);
using (var fileStream = new FileStream(path, FileMode.Create))
{
await images.ImageFile.CopyToAsync(fileStream);
}
//Edit record
try
{
_context.Update(images);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ImagesExists(images.ImageId))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(images);
}
If you want to get the ImageName only then you should use your find method withAsNoTracking(). Probably you need to change your find method on something like that :
var im = await _context.Images.AsNoTracking().SingleOrDefaultAsync(i => i.Id == id)
About AsNoTracking
It doesn't look like you programmed your action to delete the old image... Deleting the image name from the database doesn't magically delete it from your wwwroot (assuming that's where the image is saved).
You can delete an image programmatically if you have its path like this:
System.IO.File.Delete(path_of_image);
But telling from your code, it doesn't look like you have the path of the image you want to delete. To get the path, I recommend doing either one of these two (if applicable):
Adding another property in your view model for the old image path
Querying the database for the old image path before updating the record

Cannot create BitmapImage from a chosen photo from phone

I'm trying to implement a Windows Phone 8 App that works with image handling, trying it to port it from a Windows 8 App. But I got stuck quite quickly, at the beginning.
What I want to achieve is to select some pictures from the phone and show them in my app, in a similar way they look in an album. For this, I've tried some MVVM technique, also. But I'm given an error when I'm trying to create a BitmapImage from the file Stream saying I'm out of range...
Here's my model :
public class SelectedPhoto : IDisposable
{
public Stream Data { get; set; }
public string Name { get; set; }
public BitmapImage Image { get; set; }
public SelectedPhoto(string name, Stream data)
{
Name = name;
Data = new MemoryStream();
data.CopyTo(Data);
Image = new BitmapImage();
Image.SetSource(Data); //Here's the Argument Exception.
}
public void Dispose()
{
Data.Dispose();
}
}
So I'm given the exception quite in the constructor... and I use this in code in a PhotoChooserTask like this :
private void PhotoChooserTaskCompleted(object sender, PhotoResult e)
{
if (e.TaskResult == TaskResult.OK)
{
PhotosViewModel.AddPhoto(new SelectedPhoto(e.OriginalFileName, e.ChosenPhoto));
}
}
The Argument Exception says : Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection. But I'm not manipulating the Stream in any way, I just need it as it is to create the BitmapImage from it, as I've looked after some examples.
How can I get the BitmapImage of a selected image file from phone in this case? Or much better, how can I get it directly as a WriteableBitmap? Because later on, I'm planning on doing some pixel manipulation.
Any type of approach is welcome, thank you.
To fix your code, call the Seek method to go back to the beginning of your stream:
public SelectedPhoto(string name, Stream data)
{
Name = name;
Data = new MemoryStream();
data.CopyTo(Data);
Data.Seek(0, SeekOrigin.Begin);
Image = new BitmapImage();
Image.SetSource(Data); //Here's the Argument Exception.
}
That said, why are you duplicating the stream? You could directly use data.

Display image in jsp using servlet

I am working on an application where the information of users gets added and modified(updated).
In add module, admin enters user details and unique-id(abc001) gets generated at "add" button. and admin also saves the image/picture(name : abc001) of the user in server location (//some-location-ip address/D drive/images).
In "update" module, admin can modify the user details, but can not modify id.
I need some direction in couple of scenarios.
If an Admin "updates" a particular user, the image of that user which is present in the server should gets displayed on the page as soon as the admin hit the update button.
Image code in JSP :
<img height="100px;" width="100px;" src="........." alt="Candidate Image"></img>
I have written a servlet, but don't know how to call different images corresponding to different users and display the image on the profile page.
user A profile will display user A image
user B profile will display user B image
and so on
Servlet code Snippet
public class UpDatePhoto extends HttpServlet {
public UpDatePhoto () {
super();
// TODO Auto-generated constructor stub
}
private static final long serialVersionUID = -8071854868821235857L;
private static final int DEFAULT_BUFFER_SIZE = 10240; // 10KB.
private String imagePath;
*public void init() throws ServletException {
this.imagePath = "D:\\photo_not_available_large.png";
}*
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
String requestedImage = request.getPathInfo();
if (requestedImage == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404.
return;
}
File image = new File(imagePath, URLDecoder.decode(requestedImage, "UTF-8"));
String contentType = getServletContext().getMimeType(image.getName());
if (contentType == null || !contentType.startsWith("image")) {
response.sendError(HttpServletResponse.SC_NOT_FOUND); // 404.
return;
}
response.reset();
response.setBufferSize(DEFAULT_BUFFER_SIZE);
response.setContentType(contentType);
response.setHeader("Content-Length", String.valueOf(image.length()));
response.setHeader("Content-Disposition", "inline; filename=\"" + image.getName() + "\"");
BufferedInputStream input = null;
BufferedOutputStream output = null;
try {
input = new BufferedInputStream(new FileInputStream(image), DEFAULT_BUFFER_SIZE);
output = new BufferedOutputStream(response.getOutputStream(), DEFAULT_BUFFER_SIZE);
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int length;
while ((length = input.read(buffer)) > 0) {
output.write(buffer, 0, length);
}
} finally {
close(output);
close(input);
}
}
private static void close(Closeable resource) {
if (resource != null) {
try {
resource.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
The image is not http accessible but is only accessible as a file, the servlet would have to open the image file, read in the contents and place those in the response buffer" ....not sure if i am correct.
Can somebody guide me to the direction and help me out as how to fetch the image from the server directory location and display the correct image for a user.
I have a hard time in understanding the concrete problem, but I believe that your root problem is that you don't know how to set the imagePath accordingly? It has namely a wrong value.The code shows that it should be set to the root folder where all images are been placed. In the underlying operating system platform, you need to map //some-location-ip address/D drive/images as a network drive in Windows explorer, e.g. Z: and then use that in your imagePath instead.
this.imagePath = "Z:";
It also expects the image file name as request pathinfo. So, assuming that your servlet is mapped on an URL pattern of /images/*, then your <img src> should look basically like this
<img src="images/filename.png" />
You could also fill it dynamically with EL. E.g. with the unique username of the logged-in user:
<img src="images/${user.name}.png" />
As to using the "D:\\photo_not_available_large.png" replacement image, you could set that when File#exists() returns false.

Play Framework: Image Display question

ref:
http://www.lunatech-research.com/playframework-file-upload-blob
I'm uneasy about one point in this example
#{list items:models.User.findAll(), as:'user'}
<img src="#{userPhoto(user.id)}">
#{/list}
At this point I'm already holding the user object (including the image blob). Yet the userPhoto() method makes another dip into the backend to get the Image user.photo
public static void userPhoto(long id) {
final User user = User.findById(id);
notFoundIfNull(user);
response.setContentTypeIfNotSet(user.photo.type());
renderBinary(user.photo.get());
}
Any way to avoid this unnecessary findById call?
You're not actually holding the user object any more though, because the userPhoto action is invoked in a separate request that's sent when the browser tries to load the image from the URL generated by #{userPhoto(user.id)}.
Of course, you could use the cache to store data from each user's photo Blob, which would reduce the likelihood that you had to go to the database on the image request. It's more trouble than it's worth in this case though since you're just doing a simple primary key lookup for the user object, and that should be relatively inexpensive. Plus Blobs aren't serializable, so you have to pull out each piece of information separately.
Still, if you were to try that it might look something like this:
// The action that renders your list of images
public static void index() {
List<User> users = User.findAll();
for (User user : users) {
cachePhoto(user.photo);
}
render(users);
}
// The action that returns the image data to display
public static void userPhoto(long id) {
InputStream photoStream;
String path = Cache.get("image_path_user_" + id);
String type = Cache.get("image_type_user_" + id);
// Was the data we needed in the cache?
if (path == null || type == null) {
// No, we'll have to go to the database anyway
User user = User.findById(id);
notFoundIfNull(user);
cachePhoto(user.photo);
photoStream = user.photo.get();
type = user.photo.type();
} else {
// Yes, just generate the stream directly
try {
photoStream = new FileInputStream(new File(path));
} catch (Exception ex) {
throw new UnexpectedException(ex);
}
}
response.setContentTypeIfNotSet(type);
renderBinary(photoStream);
}
// Convenience method for caching the photo information
private static void cachePhoto(Blob photo) {
if (photo == null) {
return;
}
Cache.set("image_path_user_" + user.id,
photo.getFile.getAbsolutePath());
Cache.set("image_type_user_" + user.id,
photo.getType());
}
Then you'd still have to worry about appropriately populating/invalidating the cache in your add, update, and delete actions too. Otherwise your cache would be polluted with stale data.

Resources