All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.wadpam.open.web.BlobController Maven / Gradle / Ivy

The newest version!
package com.wadpam.open.web;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.google.appengine.api.blobstore.BlobInfo;
import com.google.appengine.api.blobstore.BlobInfoFactory;
import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.google.appengine.api.images.ImagesService;
import com.google.appengine.api.images.ImagesServiceFactory;
import com.google.appengine.api.images.ServingUrlOptions;
import com.wadpam.docrest.domain.RestCode;
import com.wadpam.docrest.domain.RestReturn;

/**
 * Mange blobs in GAE blobstore.
 * @author mattiaslevin
 */

@Controller
@RequestMapping(value="{domain}/blob")
public class BlobController extends AbstractRestController {
    private static final Logger LOG = LoggerFactory.getLogger(BlobController.class);

    private final BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
    private final ImagesService imagesService = ImagesServiceFactory.getImagesService();
    private static final BlobInfoFactory blobInfoFactory = new BlobInfoFactory();

    private final String HEADER_CACHE_CONTROL = "Cache-Control";
    private final String HEADER_CONTENT_DESPOSITION = "Content-Disposition";


    /**
     * Get an upload url to blobstore.
     * @param callbackPath Optional. The url that Blobstore should use as callback url.
     *                     Only set this values if you want a specific handled  method to run
     *                     after a file upload (when default behaviour is not enough)
     * @param request Optional. If set to true, the callback url will also contain the same
     *                query parameters as the this quests. Use this to forward parameters to
     *                the callback method, e.g. access_token.
     * @return a blobstore upload url
     */
    @RestReturn(value=Map.class, entity=Map.class, code={
            @RestCode(code=200, message="OK", description="Upload url created")
    })
    @RequestMapping(value="upload", method= RequestMethod.GET)
    @ResponseBody
    public Map getUploadUrl(HttpServletRequest request,
                                            @RequestParam(required=false) String callbackPath,
                                            @RequestParam(defaultValue="false") Boolean retainParams) {
        LOG.debug("Get blobstore upload url");

        // Use callback path if provided
        String callbackUrl = null != callbackPath ? callbackPath : request.getRequestURI();

        // Forward any existing query parameters, e.g. access_token
        if (retainParams) {
            final String queryString = request.getQueryString();
            callbackUrl = String.format("%s?%s", callbackUrl, null != queryString ? queryString : "");
        }

        // Response
        Map response = new HashMap();
        response.put("uploadUrl", blobstoreService.createUploadUrl(callbackUrl));

        return response;
    }

    /**
     * Upload file to blobstore callback.
     * This method will be call by the Blonstore service after a successful file upload.
     * @return the download url for each of the uploaded files
     */
    @RestReturn(value=Map.class, entity=Map.class, code={
            @RestCode(code=200, message="OK", description="File uploaded")
    })
    @RequestMapping(value="upload", method= RequestMethod.POST)
    @ResponseBody
    public Map> uploadCallback(HttpServletRequest request, @PathVariable String domain,
            @RequestParam(required = false) Integer imageSize) {
        LOG.debug("Blobstore upload callback");

        // Get all uploaded blob info records
        Map> blobInfos = blobstoreService.getBlobInfos(request);
        // Response body
        final Map> body = new TreeMap>();

        for (Entry> field : blobInfos.entrySet()) {
            // All urls for a specific upload
            ArrayList urls = new ArrayList();
            body.put(field.getKey(), urls);

            for (BlobInfo blobInfo : field.getValue()) {
                final BlobKey blobKey = blobInfo.getBlobKey();
                // serve via this BlobController
                String accessUrl = getBlobUrl(request, domain, blobKey);

                // we want to serve directly from ImagesService,
                // to avoid involving the GAE app, avoid spinning up instances,
                // and to use the awesome Google CDN.
                final String contentType = blobInfo.getContentType();

                // Valid sizes are any integer in the range [0, 1600] and is available as SERVING_SIZES_LIMIT.
                // if exceed we serve via BlobController
                if (null != contentType && contentType.startsWith("image") && null != imageSize
                        && imageSize <= ImagesService.SERVING_SIZES_LIMIT) {

                    LOG.debug(" specific image size {}", imageSize);
                    ServingUrlOptions suo = ServingUrlOptions.Builder.withBlobKey(blobKey);
                    suo = suo.imageSize(imageSize);
                    accessUrl = imagesService.getServingUrl(suo);
                }

                urls.add(accessUrl);
            }
        }

        return body;
    }

    protected static String getBlobUrl(HttpServletRequest request, String domain, BlobKey blobKey) {
        return String.format("%s://%s/api/%s/blob?key=%s", request.getScheme(), request.getHeader("Host"), domain, blobKey
                .getKeyString().toString());
    }


    /**
     * Get a blob.
     * @param key The blob store key
     * @param maxCacheAge Optional. Decides the value the Cache-Control header sent back end the response.
     *                    Default value is 1 day.
     *                    Set the 0 if set the cache directive to "no-cache" to avoid the http client
     *                    to do any caching.
     * @param asAttachment Optional. Set the Content-Disposition header to decide if the file
     *                     should be returned as an attachment. Default is false.
     * @return the blob
     */
    @RequestMapping(value="", method= RequestMethod.GET, params = "key")
    @ResponseBody
    public void getBlob(HttpServletRequest request,
            HttpServletResponse response,
            @RequestParam String key,
            @RequestParam(defaultValue="86400") int maxCacheAge,
            @RequestParam(defaultValue ="false") boolean asAttachment) throws IOException {
        LOG.debug("Get blob with key:{}", key);

        // make sure iOS caches the image (default 1 day)
        if (maxCacheAge > 0) {
            response.setHeader(HEADER_CACHE_CONTROL, String.format("public, max-age=%d", maxCacheAge));
        } else {
            response.setHeader(HEADER_CACHE_CONTROL, "no-cache");
        }

        BlobKey blobKey = new BlobKey(key);
        BlobInfo blobInfo = new BlobInfoFactory().loadBlobInfo(blobKey);
        //set response header
        response.setContentType(blobInfo.getContentType());

        if (asAttachment) {
            // Download the file as attachment
            response.setHeader(HEADER_CONTENT_DESPOSITION, String.format("filename=\"%s\"",
                    getEncodeFileName(request.getHeader("User-Agent"), blobInfo.getFilename())));
        }

        // serve blob
        blobstoreService.serve(blobKey, response);
    }


    // Encode header value for Content-Disposition
    public static String getEncodeFileName(String userAgent, String fileName) {
        String encodedFileName = fileName;
        try {
            if (userAgent.contains("MSIE") || userAgent.contains("Opera")) {
                encodedFileName = URLEncoder.encode(fileName, "UTF-8");
            } else {
                encodedFileName = "=?UTF-8?B?" + new String(Base64.encodeBase64(fileName.getBytes("UTF-8")), "UTF-8") + "?=";
            }
        } catch (Exception e) {
            LOG.error(e.getMessage());
        }

        return encodedFileName;
    }

    /**
     * Delete a blob.
     * @param  key The blob store key
     * @return 200 if successful
     */
    @RequestMapping(value="", method= RequestMethod.DELETE, params = "key")
    @ResponseBody
    public void deleteBlob(HttpServletRequest request,
            HttpServletResponse response,
            @RequestParam String key) throws IOException {
        LOG.debug("Delete blob with key:{}", key);

        BlobKey blobKey = new BlobKey(key);
        blobstoreService.delete(blobKey);
    }
    
    /***
     * getLatestBlobByName - to get file from blob service
     * 
     * @param name fileName String
     * @param response {@link HttpServletResponse}
     * @throws IOException
     */
    @RestReturn(value = Void.class, code = {@RestCode(code = 200, description = "get file latest back in response", message = "OK")})
    @RequestMapping(value = "latest", method = RequestMethod.GET, params = "name")
    public void getLatestBlobByName(@RequestParam String name, HttpServletRequest request, HttpServletResponse response)
            throws IOException {

        if (name == null || name.isEmpty()) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
        }

        final BlobInfo blob = getLatestBlobResource(name);

        // check blob found ?
        if (blob != null) {
            // serve blob
            blobstoreService.serve(blob.getBlobKey(), response);

        } else {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
        }
    }
    
    /**
    * get the latest BlobResource
    * 
    * @param name of resource
    * @return BlobInfo object
    */
   public static BlobInfo getLatestBlobResource(String name) {
       // get value in mem cache
       BlobInfo blob = null;// (BlobInfo) memCache.get(name);
       // if (blob == null) {

       Map blobsFound = new HashMap();
       // lastest date created
       long maxDate = 0;

       Iterator iterator = blobInfoFactory.queryBlobInfos();

       while (iterator.hasNext()) {
           BlobInfo blobInfo = iterator.next();

           // check name same as blobinfo filename
           if (name.equals(blobInfo.getFilename())) {
               // add element into list
               blobsFound.put(blobInfo.getCreation().getTime(), blobInfo);
               maxDate = Math.max(maxDate, blobInfo.getCreation().getTime());
           }
       }

       LOG.debug(" found blob resource name {} maxDate : {} ", name, maxDate);

       blob = blobsFound.get(maxDate);

       // add into memCache by filename
       // LOG.debug(" put blobInfo into memCache with key {}", name);
       // memCache.put(name, blob);
       // }
       // else {
       // LOG.debug(" found blob from memCache {} : {} ", name, blob.getBlobKey());
       // }
       return blob;
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy