Changeset 636


Ignore:
Timestamp:
Jul 27, 2010, 4:58:07 PM (9 years ago)
Author:
gav
Message:

Add feature to import inventory item pictures from zip file, part 2.

Location:
trunk/grails-app
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/grails-app/conf/BuildConfig.groovy

    r577 r636  
    8585        runtime ('org.apache.lucene:lucene-spellchecker:2.4.1')
    8686
     87        runtime ('org.apache.ant:ant:1.7.1')
     88        runtime ('org.apache.ant:ant-launcher:1.7.1')
     89
    8790    }
    8891
  • trunk/grails-app/i18n/messages.properties

    r635 r636  
    1515inventoryItemPictures.import.failure.no.directory=Import directory on server not found.
    1616inventoryItemPictures.import.failure.to.unzip=Failed to unzip supplied file.
     17inventoryItemPictures.import=Picture Import
     18inventoryItemPictures.import.help=A zip file of pictures (max 100MB) or singles pictures may be imported. Pictures \
     19    must have the same file names as existing inventory items.
    1720
    1821inventoryItemPurchase.import.success=Inventory item purchases imported.
  • trunk/grails-app/services/InventoryItemService.groovy

    r635 r636  
    11import org.codehaus.groovy.grails.commons.ConfigurationHolder
     2import org.apache.commons.lang.WordUtils
    23
    34/**
     
    78
    89    boolean transactional = false
     10
     11    def createDataService
     12
     13    def sessionFactory
     14    def propertyInstanceMap = org.codehaus.groovy.grails.plugins.DomainClassGrailsPlugin.PROPERTY_INSTANCE_MAP
    915
    1016    /**
     
    349355
    350356    /**
    351     * Import inventory pictures from an uploaded zip file.
     357    * Import inventory pictures from an uploaded zip file or picture.
    352358    * @param request The http request to run getFile against.
    353     * Get file should return a zip format file containing the inventory item pictures.
     359    * Get file should return a zip format file containing the inventory item pictures or a picture file.
    354360    */
    355361    def importInventoryItemPictures(request) {
     
    358364            def kByteMultiplier = 1000
    359365            def mByteMultiplier = 1000 * kByteMultiplier
    360             def fileMaxSize = 500 * mByteMultiplier
     366            def fileMaxSize = 100 * mByteMultiplier
    361367
    362368            def fail = { Map m ->
     
    367373            // Get file from request.
    368374            def multiPartFile = request.getFile('file')
    369             def zipFileName = multiPartFile.originalFilename
     375            def uploadedFileName = multiPartFile.originalFilename
    370376
    371377            if(!multiPartFile || multiPartFile.isEmpty())
     
    375381                return fail(code: "default.file.over.max.size", args: [fileMaxSize/mByteMultiplier, "MB"])
    376382
    377             // Check create import dir.
     383            // Check and create import dir.
    378384            def dir = new File(ConfigurationHolder.config.globalDirs.tempInventoryItemPicturesDirectory)
    379             def imageFiles = []
    380385
    381386            if(!dir.exists())
     
    386391            }
    387392
    388             // Write zip file to disk.
    389             def zipOutputFile = new File(dir.absolutePath + File.separator + zipFileName)
    390             multiPartFile.transferTo(zipOutputFile)
    391 
    392             // Use ant to unzip.
    393             def ant = new AntBuilder()
    394             try {
    395                 ant.unzip(  src: zipOutputFile.absolutePath,
    396                                     dest: dir.absolutePath,
    397                                     overwrite:"true" )
    398             }
    399             catch(e) {
    400                 log.error e
    401                 return fail(code:'inventoryItemPictures.import.failure.to.unzip')
    402             }
    403 
    404             // Recurse through dir building list of imageFiles.
    405             def imageFilePattern = ~/[^\s].+(\.(?i)(jpg|png|gif|bmp))$/
    406 
    407             dir.eachFileMatch(imageFilePattern) {
    408                 imageFiles << it
     393            // Write file to disk.
     394            def diskFile = new File(dir.absolutePath + File.separator + uploadedFileName)
     395            multiPartFile.transferTo(diskFile)
     396
     397            // File patterns
     398            def zipFilePattern = ~/[^\s].*(\.(?i)(zip))$/
     399            def pictureFilePattern = ~/[^\s].*(\.(?i)(jpg|png|gif|bmp))$/
     400
     401            // If file claims to be a zip file then try using ant to unzip.
     402            if(diskFile.name.matches(zipFilePattern)) {
     403                def ant = new AntBuilder()
     404                try {
     405                    ant.unzip(  src: diskFile.absolutePath,
     406                                        dest: dir.absolutePath,
     407                                        overwrite:"true" )
     408                }
     409                catch(e) {
     410                    log.error e
     411                    return fail(code:'inventoryItemPictures.import.failure.to.unzip')
     412                }
     413            }
     414
     415            // Recurse through dir building list of pictureFiles.
     416            def pictureFiles = []
     417            dir.eachFileMatch(pictureFilePattern) {
     418                pictureFiles << it
    409419            }
    410420
    411421            dir.eachDirRecurse { subDir ->
    412                 subDir.eachFileMatch(imageFilePattern) {
    413                     imageFiles << it
    414                 }
    415             }
     422                subDir.eachFileMatch(pictureFilePattern) {
     423                    pictureFiles << it
     424                }
     425            }
     426
     427            pictureFiles.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }
    416428
    417429            // Find inventoryItems by name of picture and call savePicture.
     
    419431            def itemName
    420432            def savePictureResult
    421 
    422             for(imageFile in imageFiles) {
    423                 itemName = imageFile.name[0..-5]
     433            def pictureCount = 0
     434            def picturesSavedCount = 0
     435
     436            // Turn off index mirroring.
     437            createDataService.stopSearchableIndex()
     438
     439            for(pictureFile in pictureFiles) {
     440                pictureCount++
     441
     442                if(pictureCount % 10 == 0) {
     443                    cleanUpGorm()
     444                }
     445
     446                itemName = WordUtils.capitalize(pictureFile.name[0..-5])
    424447                inventoryItemInstance = InventoryItem.findByName(itemName)
    425448                if(!inventoryItemInstance) {
     
    431454                    continue
    432455                }
    433                 savePictureResult = savePicture(inventoryItemInstance, imageFile)
     456                savePictureResult = savePicture(inventoryItemInstance, pictureFile)
    434457                if(savePictureResult.error)
    435458                    log.error savePictureResult.error
     459                else {
     460                    picturesSavedCount++
     461                    log.info 'InventoryItem picture saved: ' + itemName
     462                }
     463            }
     464
     465            // Start mirroring again and rebuild index.
     466            createDataService.startSearchableIndex()
     467
     468            log.info 'InventoryItem pictures saved: ' + picturesSavedCount
     469            log.info 'InventoryItem pictures total: ' + pictureCount
     470
     471            // Cleanup.
     472            dir.eachFile() {
     473                if(it.isDirectory())
     474                    it.deleteDir()
    436475                else
    437                     log.info 'InventoryItem picture saved: ' + itemName
     476                    it.delete()
    438477            }
    439478
     
    443482    } // importInventoryItemPictures
    444483
     484    /**
     485    * This cleans up the hibernate session and a grails map.
     486    * For more info see: http://naleid.com/blog/2009/10/01/batch-import-performance-with-grails-and-mysql/
     487    * The hibernate session flush is normal for hibernate.
     488    * The map is apparently used by grails for domain object validation errors.
     489    * A starting point for clean up is every 100 objects.
     490    */
     491    def cleanUpGorm() {
     492        def session = sessionFactory.currentSession
     493        session.flush()
     494        session.clear()
     495        propertyInstanceMap.get().clear()
     496    }
     497
    445498} // end class
  • trunk/grails-app/views/inventoryItemDetailed/importInventoryItemPictures.gsp

    r635 r636  
    1818                            <tr class="prop">
    1919                                <td valign="top" class="name">
    20                                     <label for="file">Directory:</label>
     20                                    <label for="file">File:</label>
    2121                                </td>
    2222                                <td valign="top" class="value">
    2323                                    <input type="file" id="file" name="file" size="40"/>
     24                                    <g:helpBalloon code="inventoryItemPictures.import" />
    2425                                </td>
    2526                            </tr>
Note: See TracChangeset for help on using the changeset viewer.