Java tutorial
package act.util; /*- * #%L * ACT Framework * %% * Copyright (C) 2014 - 2017 ActFramework * %% * 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. * #L% */ import act.Act; import act.app.App; import org.apache.commons.fileupload.FileItemStream; import org.osgl.$; import org.osgl.storage.ISObject; import org.osgl.storage.IStorageService; import org.osgl.storage.KeyGenerator; import org.osgl.storage.KeyNameProvider; import org.osgl.storage.impl.FileSystemService; import org.osgl.storage.impl.SObject; import org.osgl.util.C; import org.osgl.util.E; import org.osgl.util.IO; import org.osgl.util.S; import java.io.*; import java.util.Map; public class UploadFileStorageService extends FileSystemService { public static final KeyNameProvider ACT_STORAGE_KEY_NAME_PROVIDER = new KeyNameProvider() { @Override public String newKeyName() { return Act.cuid(); } }; private int inMemoryCacheThreshold; public UploadFileStorageService(Map<String, String> conf, int inMemoryCacheThreshold) { super(conf); this.setKeyNameProvider(ACT_STORAGE_KEY_NAME_PROVIDER); this.inMemoryCacheThreshold = inMemoryCacheThreshold; } public static UploadFileStorageService create(App app) { File tmp = app.tmpDir(); if (!tmp.exists() && !tmp.mkdirs()) { throw E.unexpected("Cannot create tmp dir"); } Map<String, String> conf = C.newMap("storage.fs.home.dir", Files.file(app.tmpDir(), "uploads").getAbsolutePath(), "storage.keygen", KeyGenerator.Predefined.BY_DATE.name()); conf.put(IStorageService.CONF_ID, "__upload"); conf.put("storage.storeSuffix", "false"); return new UploadFileStorageService(conf, app.config().uploadInMemoryCacheThreshold()); } public static ISObject store(FileItemStream fileItemStream, App app) { UploadFileStorageService ss = app.uploadFileStorageService(); try { return ss._store(fileItemStream); } catch (IOException e) { throw E.ioException(e); } } private ISObject _store(FileItemStream fileItemStream) throws IOException { String filename = fileItemStream.getName(); String key = newKey(filename); File tmpFile = getFile(key); InputStream input = fileItemStream.openStream(); ThresholdingByteArrayOutputStream output = new ThresholdingByteArrayOutputStream(inMemoryCacheThreshold, tmpFile); IO.copy(input, output); ISObject retVal; if (output.exceedThreshold) { retVal = getFull(key); } else { int size = output.written; byte[] buf = output.buf(); retVal = SObject.of(key, buf, size); } if (S.notBlank(filename)) { retVal.setFilename(filename); } String contentType = fileItemStream.getContentType(); if (null != contentType) { retVal.setContentType(contentType); } return retVal; } private String newKey(String filename) { if (S.blank(filename)) { return S.concat(Act.cuid(), "tmp"); } return S.pathConcat(getKey(Act.cuid()), '/', filename); } /** * The idea come from apache-commons-IO's `ThresholdingOutputStream` * * Before threshold exceeded the data is written into internal byte array buffer, once * threshold is reached then the internal byte array buffer will be dumped into the * second output stream and remaining written will be redirected to the second output * stream also */ private static class ThresholdingByteArrayOutputStream extends ByteArrayOutputStream { private int threshold; private int written; private boolean exceedThreshold; private File file; private OutputStream fileOutputStream; public ThresholdingByteArrayOutputStream(int threshold, File file) { if (threshold < 1024) { threshold = 1024; } buf = new byte[threshold]; this.threshold = threshold; this.file = $.requireNotNull(file); } @Override public synchronized void write(int b) { if (!checkThresholding(1)) { super.write(b); } else { try { fileOutputStream.write(b); } catch (IOException e) { throw E.ioException(e); } } written++; } @Override public synchronized void write(byte[] b, int off, int len) { if (!checkThresholding(len)) { super.write(b, off, len); } else { try { fileOutputStream.write(b, off, len); } catch (IOException e) { throw E.ioException(e); } } written += len; } @Override public void flush() throws IOException { if (exceedThreshold) { fileOutputStream.flush(); } } @Override public void close() throws IOException { if (exceedThreshold) { IO.close(fileOutputStream); } } byte[] buf() { return this.buf; } private boolean checkThresholding(int bytes) { if (!exceedThreshold && (written + bytes > threshold)) { exceedThreshold = true; fileOutputStream = createFileOutputStream(); try { fileOutputStream.write(buf, 0, written); } catch (IOException e) { throw E.ioException(e); } } return exceedThreshold; } private OutputStream createFileOutputStream() { File dir = file.getParentFile(); if (!dir.exists() && !dir.mkdirs()) { throw E.ioException("Cannot create dir: " + dir.getAbsolutePath()); } return IO.buffered(IO.outputStream(file)); } } }