I am saving image files in my web folder. But at the time of saving or suppose a user want to change his picture than i want to delete the old picture and save the new one with the same file name. But I am failing after trying. Can anyone please help me on this please? Here is all my action below :
my save action >>>
def savePicture = {
String message = ""
def user = User.findById(1)
def userId = user.id
MultipartHttpServletRequest mpr = (MultipartHttpServletRequest)request;
CommonsMultipartFile f = (CommonsMultipartFile) mpr.getFile("productPic");
def okcontents = ['image/png', 'image/jpeg', 'image/gif']
if (! okcontents.contains(f.getContentType())) {
message = "Avatar must be one of: ${okcontents}"
render(view:'uploadForm', model:[message: message])
return;
}
String type = f.getContentType().substring(6)
String baseImageName = java.util.UUID.randomUUID().toString();
baseImageName = "user${user.id}"
// Saving image in a folder assets/channelImage/, in the web-app, with the name: baseImageName
def downloadedFile = f //.getFile( "product.baseImage" )
String fileUploaded = fileUploadService.uploadFile( downloadedFile, "${baseImageName}.${type}", "assets/channelImage/" )
if( fileUploaded ){
user.avatarType = type
user.save()
message = "File Saved Successfully."
redirect(action: 'show', params: [userId: userId])
}
}
my service action where I am trying to delete before save >>>
def String uploadFile( MultipartFile file, String name, String destinationDirectory ) {
def serveletContext = ServletContextHolder.servletContext
def storagePath = serveletContext.getRealPath( destinationDirectory )
def storagePathDirectory = new File("${storagePath}/${name}").delete()
// Store file
if(!file.isEmpty()){
file.transferTo( new File("${storagePath}/${name}") )
println("Saved File: ${storagePath}/${name}")
return "${storagePath}/${name}"
}else{
println "File: ${file.inspect()} was empty"
return null
}
}
my show method in controller >>>
def show = {
Long uid = Long.parseLong(params.userId)
def avatarUser = User.get(uid)
String link = "user${avatarUser.id}.${avatarUser.avatarType}"
[link:link]
}
my view page >>>
<g:if test="${link}">
<img src="${resource(dir: 'assets/channelImage', file: "${link}")}" />
</g:if>
Related
I am new to grails and developing a web application such that a user updates profile details along with an avatar. I have a user controller that contains the logic to upload and save the avatar image. Unfortunately after choosing the avatar then upload, I get the this error which has troubled me for days:
No signature of method: eafya.User.current() is applicable for argument types: (grails.web.servlet.mvc.GrailsHttpSession) values: [Session Content: org.grails.FLASH_SCOPE = org.grails.web.servlet.GrailsFlashScope#d7b602 loggedOnUser = eafya.Patient : 1 messages = [] loginAudit = eafya.LoginAudit : 19 ] Possible solutions: count(), create(), ident(), collect(), insert(), collect(groovy.lang.Closure)
here is the controller:
package eafya
class UserController {
def current(){}
def select_avatar(){
}
def upload_avatar() {
def user = User.current(session) // or however you select the current user
// Get the avatar file from the multi-part request
def f = request.getFile('avatar')
// List of OK mime-types
def okcontents = ['image/png', 'image/jpeg', 'image/gif']
if (! okcontents.contains(f.getContentType())) {
flash.message = "Avatar must be one of: ${okcontents}"
render(view:'select_avatar', model:[user:user])
return;
}
// Save the image and mime type
user.avatar = f.getBytes()
user.avatarType = f.getContentType()
log.info("File uploaded: " + user.avatarType)
// Validation works, will check if the image is too big
if (!user.save()) {
render(view:'select_avatar', model:[user:user])
return;
}
flash.message = "Avatar (${user.avatarType}, ${user.avatar.size()} bytes) uploaded."
redirect(action:'show')
}
def avatar_image() {
def avatarUser = User.get(params.id)
if (!avatarUser || !avatarUser.avatar || !avatarUser.avatarType) {
response.sendError(404)
return;
}
response.setContentType(avatarUser.avatarType)
response.setContentLength(avatarUser.avatar.size())
OutputStream out = response.getOutputStream();
out.write(avatarUser.avatar);
out.close();
}
}
I am trying to save an uploaded file into the file system directory, and allow other users to download it.
I am currently saving it in my database and not in my file system directory. Here is my code:
class Document {
String filename
byte[] filedata
Date uploadDate = new Date()
static constraints = {
filename(blank: false, nullable:false)
filedata(blank: true, nullable: true, maxSize:1073741824)
}
}
and my controller for uploading the file is:
class DocumentController {
static allowedMethods = [delete: "POST"]
def index = {
redirect(action: "list", params: params)
}
def list() {
params.max = 10
[documentInstanceList: Document.list(params), documentInstanceTotal: Document.count()]
}
def uploadPage() {
}
def upload() {
def file = request.getFile('file')
if(file.isEmpty())
{
flash.message = "File cannot be empty"
}
else
{
def documentInstance = new Document()
documentInstance.filename = file.getOriginalFilename()
documentInstance.filedata = file.getBytes()
documentInstance.save()
}
redirect (action: 'list')
}
}
I think you could do a fuction similar to the one below:
boolean upload(MultipartFile uploadFile, String fileUploadDir){
String uploadDir = !fileUploadDir.equals('') ?: 'C:/temp' //You define the path where the file will be saved
File newFile = new File("$uploadDir/${uploadFile.originalFilename}"); //You create the destination file
uploadFile.transferTo(newFile); //Transfer the data
/**You would need to create an independent Domain where to store the path of the file or have the path directly in your domain*/
}
Since you will only need to save the path of the file you could add a string to your domain to store it or you could create an independent domain to store the data of your file. You will also need to add try/catch statements where needed.
And to retrieve the file you would need to add to your controller something like the next code:
File downloadFile = new File(yourFileDomain?.pathProperty) //get the file using the data you saved in your domain
if(downloadFile){ //Set your response properties
response.characterEncoding = "UTF-8"
response.setHeader "Content-disposition", "attachment; filename=\"${yourFileDomain?.fileNameProperty}\"" //add the header with the filename you saved in your domain you could also set a default filename
//response.setHeader "Content-disposition", "attachment; filename=\"myfile.txt\""
response.outputStream << new FileInputStream(downloadFile)
response.outputStream.flush()
return
}
Hope this helps, any comments are welcome.
I have a page with dynamic list boxes(selecting value from the first list populates the values in the second list box).
The validation errors for the list boxes are working fine, but while displaying the error messages the page is getting refreshed and the selected values are been set to initial status(need to select the values again in the list boxes)
The page is designed to add any number of list boxes using ajax calls, so adding and selecting the values again is going to be a rework.
Could you help me in displaying the validation errors and keeping the selected values as they are(previously I faced a similar situation which was resolved by replacing local variables of preprocess and postprocess with a global variable, this time no luck with that approach)
Any hints/help would be great
static constraints = {
deviceMapping(
validator: {val, obj ->
Properties dm = (Properties) val;
def deviceCheck = [:];
if (obj.customErrorMessage == null) {
for (def device : dm) {
if (device.key == null || "null".equalsIgnoreCase(device.key)) {
return ["notSelected"];
}
deviceCheck.put(device.key, "");
}
if (deviceCheck.size() != obj.properties["numberOfDevices"]) {
return ["multipleDevicesError"];
}
}
}
)
customErrorMessage (
validator: {
if ("sameDeviceMultipleTimes".equals(it)) {
return ['sameDeviceMultipleTimes']
}
}
)
}
public LinkedHashMap<String, Object> preProcess(sessionObject, params, request) {
Submission submission = (Submission) sessionObject;
def selectedFileName = sessionObject.fileName;
logger.debug("submission.deviceMapping :"+submission.deviceMapping)
try {
Customer customer = Customer.get(submission.customerId);
OperatingSystem operatingSystem = OperatingSystem.get(submission.operatingSystemId)
def ftpClientService = new FtpClientService();
def files = ftpClientService.listFilesInZip(customer.ftpUser, customer.ftpPassword, customer.ftpHost, customer.ftpToPackageDirectory, selectedFileName, operatingSystem, customer.ftpCustomerTempDirectory);
def terminalService = new TerminalService();
OperatingSystem os = OperatingSystem.get(submission.getOperatingSystemId());
def manufacturers = terminalService.getAllDeviceManufacturersForType(os.getType());
logger.debug("manufacturers after os type :"+manufacturers)
logger.debug("files in preprocess :"+files)
def devicesForFiles = [:]
files.each { file ->
def devicesForThisFile = [];
submission.deviceMapping.each { device ->
if (device.value == file.fileName) {
String manufacturer = terminalService.getManufacturerFromDevice("${device.key}");
def devicesForManufacturer = terminalService.getDevicesForManufacturerAndType(manufacturer, os.getType());
devicesForThisFile.push([device:device.key, manufacturer: manufacturer, devicesForManufacturer: devicesForManufacturer]);
}
}
devicesForFiles.put(file.fileName,devicesForThisFile);
}
logger.debug("devicesForFiles :"+devicesForFiles)
return [command: this, devicesForFiles: devicesForFiles, files: files, manufacturers: manufacturers];
} catch (Exception e) {
logger.warn("FTP threw exception");
logger.error("Exception", e);
this.errors.reject("mapGameToDeviceCommand.ftp.connectionTimeOut","A temporary FTP error occurred");
return [command: this];
}
}
public LinkedHashMap<String, Object> postProcess(sessionObject, params, request) {
Submission submission = (Submission) sessionObject;
Properties devices = params.devices;
Properties files = params.files;
mapping = devices.inject( [:] ) { map, dev ->
// Get the first part of the version (up to the first dot)
def v = dev.key.split( /\./ )[ 0 ]
map << [ (dev.value): files[ v ] ]
}
deviceMapping = new Properties();
params.files.eachWithIndex { file, i ->
def device = devices["${file.key}"];
if (deviceMapping.containsKey("${device}")) {
this.errors.reject("You cannot use the same device more than once");
return [];
//customErrorMessage = "sameDeviceMultipleTimes";
}
deviceMapping.put("${device}", "${file.value}");
}
if (params.devices != null) {
this.numberOfDevices = params.devices.size(); //Used for the custom validator later on
} else {
this.numberOfDevices = 0;
}
//logger.debug("device mapping :"+deviceMapping);
submission.deviceMapping = mapping;
return [command: this, deviceMapping: mapping, devicesForFiles: devicesForFiles ];
}
}
The problem is in your gsp page. Be sure that all field are initialised with a value
<g:text value="${objectInstance.fieldname}" ... />
Also the way it is selecting values is through id, so be sure to set it as well:
<g:text value="${objectInstance.fieldname}" id=${device.manufacturer.id} ... />
I hope some one can help me out here. I am trying to create an upload program that can upload one or more mp3 files and add their attributes to a database (song title, album, etc). I'm not sure if the problems I am encountering is because of a database issue or because I don't really understand how CommonsMultipartFile works. Anyway, here is my code"
View:
<g:form name="fileupload" url="[action:'uploads',controller:'fileResource']" method="POST" enctype="multipart/form-data">
<span>Add files...</span>
<input type="file" name="files" multiple>
</g:form>
fileResource:
def uploads = {
Collection result = []
if (request instanceof MultipartHttpServletRequest) {
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest)request;
CommonsMultipartFile file = (CommonsMultipartFile)multiRequest.getFile("files");
println"one"
moveFile(file)
println"three"
result << [name: file.originalFilename, size:file.size]
}
render result as JSON
}
private moveFile(CommonsMultipartFile file){
println"two"
try{
def userId = getUserId()
def newFileName = java.util.UUID.randomUUID().toString()
def userGuid = SecUser.get(userId.id).uid
def webRootDir = servletContext.getRealPath("/")
File myFile = new File( webRootDir + "/myUsers/${userGuid}/music",newFileName + ".")
file.transferTo(myFile)
MP3File mp3file = new MP3File(myFile);
String duration = mp3file.getAudioHeader().getTrackLength()
String bitRate = mp3file.getAudioHeader().getBitRate()
String encodingType = mp3file.getAudioHeader().getEncodingType()
String getFormat = mp3file.getAudioHeader().getFormat()
String trackLength = mp3file.getAudioHeader().getTrackLength()
String fileName = file.originalFilename
String artistName = ""
String albumName = ""
String songName = ""
def getArtistId=""
try{
if (mp3file.hasID3v2Tag()){
//println("has id3v2 tag")
ID3v24Tag id3v24tag = mp3file.getID3v2TagAsv24();
artistName = (id3v24tag.getFirst(ID3v24Frames.FRAME_ID_ARTIST).trim())
albumName = (id3v24tag.getFirst(ID3v24Frames.FRAME_ID_ALBUM).trim())
songName = (id3v24tag.getFirst(ID3v24Frames.FRAME_ID_TITLE).trim())
}else if (mp3file.hasID3v1Tag()) {
//println("has id3v1 tag")
ID3v1Tag tag = mp3file.getID3v1Tag();
artistName = tag.getFirst(FieldKey.ARTIST).trim()
albumName = tag.getFirst(FieldKey.ALBUM).trim()
songName = tag.getFirst(FieldKey.TITLE).trim()
}
else{
println("this format not yet supported:" + getFormat)
}
}catch (Exception ex) {
log.error("ERROR: FILE NOT PROCESSED")
}
try{
def oArtist = ""
def c = Artist.createCriteria()
def getMyArtist = c.get {
eq('artistName', artistName )
secUser {
eq('id', userId.id)
}
}
if (getMyArtist == null){
//artist does not exist
// 1c. add artist
// 1d. add album
// 1e. add song
// 1f. move song
// 1g. DONE
oArtist= new com.jason.score.Artist(
artistName : artistName ,
secUser : userId.id
)
userId.addToArtist(oArtist).save(flush:true)
def oAlbum = new Album
(
albumName: albumName
)
oArtist.addToAlbum(oAlbum).save(flush:true)
def oSong = new Song
(
fileLocation: webRootDir + "myUsers/${userGuid}/music/",
songBitRate: bitRate,
songDuration: duration,
songEncodeType: encodingType,
songName: songName
)
oAlbum.addToSong(oSong).save(flush:true)
}else{
//artist exists with that username
//need album name and artist id
def d = Album.createCriteria()
def getMyAlbum = d.get {
eq('albumName', albumName )
artist {
eq('id', getMyArtist.id)
}
}
if(getMyAlbum == null){
// 3. add album
// 3a. add song
// 3b. move song
// 3c. DONE
Artist findArtist = Artist.get(getMyArtist.id)
def oAlbum = new Album
(
albumName: albumName
)
findArtist.addToAlbum(oAlbum).save(flush:true)
def oSong = new Song
(
fileLocation: webRootDir + "myUsers/${userGuid}/music/",
songBitRate: bitRate,
songDuration: duration,
songEncodeType: encodingType,
songName: songName
)
oAlbum.addToSong(oSong).save(flush:true)
}else{
//album does exist
//check if song exists with album id
def e = Song.createCriteria()
def getMySong = e.get {
eq('songName', songName )
album {
eq('id', getMyAlbum.id)
}
}
if (getMySong==null){
Album findAlbum = Album.get(getMyAlbum.id)
def oSong = new Song
(
fileLocation: webRootDir + "myUsers/${userGuid}/music/",
songBitRate: bitRate,
songDuration: duration,
songEncodeType: encodingType,
songName: songName
)
findAlbum.addToSong(oSong).save(flush:true)
}else{
//it already exists, so do nothing
}
}
}
}catch (Exception ex){
log.error("ERROR: ADDING TO DB: " + ex)
}
}catch (Exception ex){
log.error("error" + ex)
}
}
private getUserId(){
return SecUser.get(springSecurityService.principal.id)
}
private getUser(){
return SecUser.get(springSecurityService.principal)
}
The error I am getting is:
2012-01-02 14:25:25,314 [http-8080-1] ERROR events.PatchedDefaultFlushEventListener - Could not synchronize database state with session
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.jason.score.SecUser#1]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1792)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2435)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2335)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2635)
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:115)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:263)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:168)
at org.codehaus.groovy.grails.orm.hibernate.events.PatchedDefaultFlushEventListener.performExecutions(PatchedDefaultFlushEventListener.java:46)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027)
at org.springframework.orm.hibernate3.HibernateTemplate$28.doInHibernate(HibernateTemplate.java:883)
at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:406)
at org.springframework.orm.hibernate3.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:374)
at org.springframework.orm.hibernate3.HibernateTemplate.flush(HibernateTemplate.java:881)
at org.codehaus.groovy.grails.orm.hibernate.metaclass.SavePersistentMethod$1.doInHibernate(SavePersistentMethod.java:58)
at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:406)
at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:339)
at org.codehaus.groovy.grails.orm.hibernate.metaclass.SavePersistentMethod.performSave(SavePersistentMethod.java:53)
at org.codehaus.groovy.grails.orm.hibernate.metaclass.AbstractSavePersistentMethod.doInvokeInternal(AbstractSavePersistentMethod.java:179)
at org.codehaus.groovy.grails.orm.hibernate.metaclass.AbstractDynamicPersistentMethod.invoke(AbstractDynamicPersistentMethod.java:59)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSite.invoke(PojoMetaMethodSite.java:188)
....
2012-01-02 14:25:25,379 [http-8080-1] ERROR errors.GrailsExceptionResolver - Exception occurred when processing request: [POST] /com.jason.score/fileResource/uploads
Stacktrace follows:
org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException: Object of class [com.jason.score.SecUser] with identifier [1]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.jason.score.SecUser#1]
at java.lang.Thread.run(Thread.java:662)
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.jason.score.SecUser#1]
... 1 more
The error is due to Hibernate. There is likely an error in your criteria queries. This looks like a likely candidate
oAlbum.addToSong(oSong).save(flush:true)
Those should be separate operations. The criteria queries should be encapsulated by methods in the service layer. For operations this simple, you probably won't see much benefit from using criteria over GORM's dynamic finders.
You're also trying to do way too much in one place. Refactor your controller to use a service to do these operations. Break moveFile() up into multiple methods to parse the MP3File, then pass that to another method to add it to the user's songs.
You shouldn't need to use the user's ID if you create the relationship directly in the domain.
Write some tests for each atomic piece and it should make the error easier to find.
I am entering values(URLs) in the text box and when I click the next button, validation runs whether the entered url values are valid or not.
if the urls are not valid then i display the error as below
if(valid == false){
this.errors.reject("Incorrect URL is entered for "+countries.get(countryCode)+" - please ensure to use a correct URL for More Games.")
return [];
}
Once the error is shown, its clearing the entered values and need to enter the values again. How can I restrict clearing the values(i think the page is getting loaded again) or to restrict the page to load again.
here is the command -
class DeliveryMoreGamesURLCommand implements Command {
private static final LOG = LogFactory.getLog(DeliveryController.class)
def wrapperService = ApplicationHolder.application.getMainContext().getBean("wrapperService");
def customerService = ApplicationHolder.application.getMainContext().getBean("customerService");
def countryService = ApplicationHolder.application.getMainContext().getBean("countryService");
def helperService = ApplicationHolder.application.getMainContext().getBean("helperService");
def ascService = ApplicationHolder.application.getMainContext().getBean("ascService");
Hashtable<String, String> countries;
public LinkedHashMap<String, Object> preProcess(sessionObject, params, request) {
Delivery delivery = (Delivery) sessionObject;
def customer = Customer.get(delivery.customerId);
def contentProvider = ContentProvider.get(delivery.contentProviderId);
def deployment = Deployment.get(delivery.deploymentId);
def operatingSystem = OperatingSystem.get(delivery.operatingSystemId);
countries = wrapperService.getCountries(deployment, operatingSystem);
def sortedCountries = countries.sort { a, b -> a.value <=> b.value };
def urls = ascService.getMoreGamesUrlsPerTemplates(deployment, operatingSystem);
def moreGamesUrls = new Hashtable<String,String>();
countries.each { country ->
String countryCode = countryService.getTwoLetterCountryAbbreviation(country.key);
String url = customerService.getMoreGamesUrl(customer, contentProvider, countryCode);
if ("".equals(url)) {
url = urls.get(country.key);
if (url == null) {
url = "";
}
}
moreGamesUrls.put(country.key, url); // We need to use the existing country code if the channels are deployed with three letter country codes
}
return [command: this, countries: sortedCountries, moreGamesUrls: moreGamesUrls]
}
public LinkedHashMap<String, Object> postProcess(sessionObject, params, request) {
Delivery delivery = (Delivery) sessionObject;
def urls = params.gamesUrls;
LOG.debug("urls from gsp :"+urls)
try{
urls.eachWithIndex { u, i ->
String countryCode = u.key;
String url = urls["${u.key}"]
if(url != ""){
Boolean valid = helperService.isURLValid(url)
if(valid == false){
this.errors.reject("Incorrect URL is entered for "+countries.get(countryCode)+" - please ensure to use a correct URL for More Games.")
return [];
}
}
}
}catch (Exception ex) {
logger.warn("Incorrect URL is entered", ex)
return [];
}
def moreGamesUrls = new Hashtable<String, String>();
urls.eachWithIndex { u, i ->
String countryCode = u.key;
String url = urls["${u.key}"]
moreGamesUrls.put(countryCode, url);
}
delivery.countries = countries;
delivery.moreGamesUrls = moreGamesUrls;
LOG.debug("moreGamesUrls after edit=${delivery.moreGamesUrls}");
return null;
}
}
from the command preprocess the data will be rendered and after clicking the next button, the postprocess will be invoked and the validation for the url...
Resolved this issue by implementing moreGamesUrls as a global variable(which stored the values even after the error is thrown)