Java tutorial
/** * Copyright 2013 Ben Navetta <ben@bennavetta.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.bennavetta.appsite.file; import static com.google.common.base.Preconditions.checkNotNull; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import com.google.appengine.api.blobstore.BlobstoreInputStream; import com.google.appengine.api.files.AppEngineFile; import com.google.appengine.api.files.FileReadChannel; import com.google.appengine.api.files.FileService; import com.google.appengine.api.files.FileServiceFactory; import com.google.appengine.api.files.FileWriteChannel; import com.google.appengine.api.files.LockException; import com.google.common.net.MediaType; import com.netflix.config.DynamicIntProperty; import com.netflix.config.DynamicPropertyFactory; @Service public class ResourceService { private Logger log = LoggerFactory.getLogger(getClass()); private DynamicIntProperty bufferSize = DynamicPropertyFactory.getInstance().getIntProperty("buffer.size", 1024 * 8); private FileService fs = FileServiceFactory.getFileService(); public Resource create(String path, MediaType mime, ReadableByteChannel src) throws IOException { String normalized = PathUtils.normalize(path); log.debug("Creating resource {}", normalized); AppEngineFile file = fs.createNewBlobFile(mime.toString(), normalized); FileWriteChannel channel = fs.openWriteChannel(file, true); MessageDigest digest = null; try { digest = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("MD5 not available", e); } ByteBuffer buf = ByteBuffer.allocateDirect(bufferSize.get()); while (src.read(buf) != -1 || buf.position() != 0) { buf.flip(); int read = channel.write(buf); if (read != 0) { int origPos = buf.position(); int origLimit = buf.limit(); buf.position(origPos - read); buf.limit(origPos); digest.update(buf); buf.limit(origLimit); buf.position(origPos); } buf.compact(); } channel.closeFinally(); Resource resource = new Resource(normalized, fs.getBlobKey(file).getKeyString(), digest.digest(), mime); resource.save(); return resource; } public void download(Resource resource, WritableByteChannel dest) throws IOException { AppEngineFile file = fs.getBlobFile(resource.blobKey()); try (FileReadChannel channel = fs.openReadChannel(file, false);) { copy(channel, dest); } } public ReadableByteChannel read(Resource resource) throws FileNotFoundException, LockException, IOException { checkNotNull(resource); log.debug("Reading {}", resource); AppEngineFile file = checkNotNull(fs.getBlobFile(resource.blobKey())); return fs.openReadChannel(file, false); } public InputStream readStream(Resource resource) throws IOException { checkNotNull(resource); return new BlobstoreInputStream(checkNotNull(resource.blobKey())); } private void copy(ReadableByteChannel src, WritableByteChannel dest) throws IOException { ByteBuffer buffer = ByteBuffer.allocateDirect(bufferSize.get()); while (src.read(buffer) != -1 || buffer.position() != 0) { buffer.flip(); dest.write(buffer); buffer.compact(); } } }