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.
Related
I am using Orchard and would like to cache data specific to an authenticated user.
When a new user logs in, or after a period of time, the database should be queried again.
I've accomplished half of this below (after 30 minutes it will query the database again):
private UserData SomeUserSpecificData()
{
var data = _cacheManager.Get("userdata",
ctx => {
ctx.Monitor(_clock.When(TimeSpan.FromMinutes(30)));
return GetDatabaseData();
});
return data;
}
But how would I force the cache to re-query the database when a new user has logged in?
I have a feeling it might involve ISignals but not sure how to implement this.
Thanks.
You're absolutely right with ISignals!
You just need to inject ISignals and use it like this:
private readonly Orchard.Caching.ISignals _signals;
private UserData SomeUserSpecificData()
{
var data = _cacheManager.Get("userdata",
ctx => {
ctx.Monitor(_clock.When(TimeSpan.FromMinutes(30)));
cts.Monitor(_signals.When("SOMETHING_HAPPENED_FOR_xyz"));
return GetDatabaseData();
});
return data;
}
What's also nice to know: You can monitor as many signals as you want.
To keep things clean I'll typically create a static class like this:
public static class CacheSignals
{
public const string SomethingHappened = "SOMETHING_HAPPENED";
public const string SomethingHappenedForUser(int userId) => string.Format("SOMETHING_HAPPENED_FOR_{0}", userId);
}
Now you can simply implement a custom EventHandler forOrchard.Users.Events.IUserEventHandler
and invalidate the cache on LoggedIn.
private readonly Orchard.Caching.ISignals _signals;
public void LoggedIn(Security.IUser user)
{
_signals.Trigger(CacheSignals.SomethingHappenedForUser(user.Id));
}
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.
I have some custom fields on my User object that I want to access with APEX code in my VisualForce trigger. When I access it from a Formula field I get to use a nifty $User reference like this:
$User.my_prop__c
From APEX I have to query the User object by UserId like this:
[select my_prop__c from User where id = :UserInfo.getUserId()].my_prop__c;
Is there something baked into APEX already that would let me get at the user properties without the SOQL query? If not, does anyone know of a utility class for lazy loading and caching user properties so the overhead is minimal.
I would use something similar to the following code sample. It uses a singleton pattern to statically store the information in memory for the duration of your transaction. It's similar to the lazy loading that twamley proposed but I feel this is a much simpler approach.
Usage 1: UserUtil.CurrentUser.Email;
Usage 2: User someUser = UserUtil.getUser(someUserId);
This will allow you to access the same information on the current user or other users in the system. Notice the queryUsers method just returns a query result. This makes it easy to add and remove fields from your query as it is isolated in its own method keeping things simple.
Note: that this code pulls in all users when used. Most orgs do not have multiple hundreds of users so heap size shouldn't be a concern. But if it is you can just modify the queryUsers() method to only return active users or filter down based on other criteria.
public class UserUtil {
//Protected Members
private static final UserUtil instance = new UserUtil();
private Map<Id, User> mapUsers;
//Properties
public static User CurrentUser {
get { return getUser(UserInfo.getUserId()); }
}
//Constructor
private UserUtil() {
mapUsers = new Map<Id, User>(queryUsers());
}
//Public Methods
public static User getUser(Id userId) {
if (instance.mapUsers.containsKey(userId)) {
return instance.mapUsers.get(userId);
}
else {
throw new InvalidUserIdException('Unable to locate user id: ' + userId);
}
}
//Private Methods
private List<User> queryUsers() {
return [SELECT
Id
, Name
, UserName
, Email
, Alias
FROM
User];
}
//Internal Classes
public class InvalidUserIdException extends Exception {}
}
I wrote my own utility class. I'm still interested in better techniques though.
This utility class lazy loads when the first property is accessed. Update_Closed_Won_Opportunities__c and Set_Opportunities_to_Closed_Won__c are my custom fields on the User object (visible only to System Administrators so people can't upgrade their permissions).
public with sharing class MyUserInfo {
private Id userId;
private User myUser; // Hold onto the user object once we've loaded it
// Default constructor uses the active user id
public MyUserInfo() {
userId = UserInfo.getUserId();
}
// Secondary constructor accepts a user id as a parameter
public MyUserInfo(Id someOtherUserId) {
userId = someOtherUserId;
}
// Only called one time when we first need it so grab all of the custom fields now
private void LazyLoadUser() {
System.AssertNotEquals(null, userId);
myUser = [
SELECT Update_Closed_Won_Opportunities__c, Set_Opportunities_To_Closed_Won__c
FROM User
WHERE id = :userId
];
System.AssertNotEquals(null, myUser, 'Unable to load user with id ' + userId); // could return defaults instead
}
// Getters (be sure to include each field in the SOQL of LazyLoadUser)
public boolean UpdateClosedWonOpportunities { get {
if (myUser == null) LazyLoadUser();
return myUser.Update_Closed_Won_Opportunities__c;
} }
public boolean SetOpportunitiesToClosedWon { get {
if (myUser == null) LazyLoadUser();
return myUser.Set_Opportunities_To_Closed_Won__c;
} }
}
Here is my trigger utilizing that class. The first line myUserInfo = new MyUserInfo(); doesn't run any SOQL. That won't happen until the first custom get property is used. Subsequent calls don't need SOQL.
trigger LockClosedOpportunity on Opportunity (before update) {
MyUserInfo myUserInfo = new MyUserInfo();
for (Opportunity o : trigger.old)
{
if (!myUserInfo.UpdateClosedWonOpportunities && o.StageName == 'Closed Won')
trigger.newMap.get(o.Id).addError('You do not have permission to change an Opportunity after it has been set to Closed Won.');
}
for (Opportunity o : trigger.new)
{
if ( !myUserInfo.SetOpportunitiesToClosedWon && o.StageName == 'Closed Won' && trigger.oldMap.get(o.Id).StageName != 'Closed Won' )
o.addError('You do not have permission to set an Opportunity to Closed Won.');
}
}
It reads similar to $User in formulas and I don't have to worry about tacking on multiple SOQL calls when one (or zero) suffices.
I have an issue with my model in EF model first. I have a web app, with security layer and entity layer and mvc3 layer. So in each layer I put a instance of my context db (one for each class in the main declaration section). the problem is when y find any user of my db for validate the login process, simple search, not make any changes in entity, is like this:
var usr = db.Usuarios.First(user => user.UserName.Equals(userName));
If I change the password for example in other layer (in my controller user), and the logout an login again, the linq search (see code up) always return the old password. This is for the context db dont dispose and dont go to database for the data, simple load the user for the model.
So, how I can force the model to refresh the data from the database?
I try, put lazy load in false but not work.
db.Configuration.LazyLoadingEnabled = false;
partial work if I decalre a instance of my context in the metod to validate passwork and dispose this instance, but I think it is the best practice.
like that:
public static bool ValidateUser(string userName, string password)
{
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
{
var dbtmp = new ConorContainer();
var usr = dbtmp.Usuarios.First(user => user.UserName.Equals(userName));
if (usr != null)
{
var passDescrypt = Decript(usr.Password);
dbtmp.Dispose();
return passDescrypt.Equals(password);
}
return false;
}
return false;
}
You have to decide the lifetime of the context in each case. It is preferred to use short lived contexts in web applications specially in static methods.
Your method can be improved by employing the using block that will call the Dispose method when going out of scope.
public static bool ValidateUser(string userName, string password)
{
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
{
using(var dbtmp = new ConorContainer())
{
var usr = dbtmp.Usuarios.First(user => user.UserName.Equals(userName));
if (usr != null)
{
var passDescrypt = Decript(usr.Password);
return passDescrypt.Equals(password);
}
}
return false;
}
return false;
}
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.