StorageItem life cycle
Dari provides two callbacks for file-saving events: before saving the file and after saving the file.
The beforeSave event, defined by Interface StorageItemBeforeSave, occurs immediately prior to saving a file. In a typical scenario, you interrupt this event as part of processing a file passed to the doRequest method available in Class StorageItemFilter.
A best practice in a web application that allows file uploads is to verify that the uploaded file is of the type expected. For example, if you allow users to upload an image, you should verify that the file is indeed an image and not a text or PDF file. The following example ensures that an uploaded file has a permitted MIME type for images.
import com.psddev.dari.util.StorageItem;
import com.psddev.dari.util.StorageItemBeforeSave;
import com.psddev.dari.util.StorageItemUploadPart;
public class LocalStorageItemBeforeSave implements StorageItemBeforeSave {
@Override
public void beforeSave(StorageItem storageItem, StorageItemUploadPart storageItemUploadPart) throws IOException {
List<String> allowedTypes = new ArrayList<>();
allowedTypes.add("image/jpeg");
allowedTypes.add("image/png");
allowedTypes.add("image/gif");
String uploadedType = storageItem.getContentType();
if (!allowedTypes.contains(uploadedType)) {
throw new IOException("[" + uploadedType + "] is not one of the allowed MIME types!");
}
}
}
-
Constructs a list of allowed MIME types for an uploaded file.
-
Retrieves the MIME type of the uploaded file.
-
Performs exception handling if the uploaded file does not have a permitted MIME type.
The afterSave event, defined by Interface StorageItemAfterSave, occurs immediately after saving a file. In a typical scenario, you interrupt this event as part of processing a file passed to the doRequest method available in Class StorageItemFilter.
Some project implementations make backups of uploaded files, a good strategy to increase reliability or for forensic investigations. The following example copies a saved file to another disk at a particular mount point.
import com.psddev.dari.util.Settings;
import com.psddev.dari.util.StorageItem;
import com.psddev.dari.util.StorageItemAfterSave;
import org.apache.commons.io.FileUtils;
public class LocalStorageItemAfterSave implements StorageItemAfterSave {
@Override
public void afterSave(StorageItem storageItem) throws IOException {
String staticPath = Settings.get(String.class, "dari/storage/localstorage/rootPath");
String dynamicPath = storageItem.getPath();
String fullPath = staticPath + "/" + dynamicPath;
File source = new File(fullPath);
File dest = new File("/path/to/mount/point/" + dynamicPath);
try {
FileUtils.copyFile(source, dest);
} catch (IOException e) {
e.printStackTrace();
}
}
}
-
Retrieves the static portion of the path where Dari saved the file.
-
Retrieves the dynamic portion of the path, including the file name, where Dari saved the file. (For an explanation of static and dynamic portions of storage item paths, see the illustration "Static and dynamic portions of StorageItem paths.")
-
Combines the static path with the dynamic path to get the full path to the source file.
-
Constructs a new destination path for the copied file. The static portion is the hard-coded path to the mount point, and the dynamic portion is a replica from the source file.
For example, suppose the Tomcat configuration includes the following entry:
<Environment name="dari/storage/localstorage/rootPath" type="java.lang.String" value="/home/dariuser/storage" />
Also suppose the following:
- Your application uses a path generator that creates a dynamic path roses/are/red/.
- You uploaded the file dragon-slayer.jpg.
- Your backup disk is mounted at /mnt/huge-backup-disk/
The previous snippet is equivalent to the following command:
cp /home/dariuser/storage/roses/are/red/dragon-slayer.jpg /mnt/huge-backup-disk/roses/are/red/dragon-slayer.jpg
Referring to the illustration "Static and dynamic portions of StorageItem paths," Dari creates a default dynamic path of the form xx/xx/xxxxxxxxxxxxxxxxxxxxxxxxxxxx/ that is unique for each saved storage item. The dynamic path is derived from a random Java UUID.
You can customize how Dari generates the dynamic path, such as using a different random-number generator or even generating a static directory structure. In the following example you implement Interface StorageItemPathGenerator to create a directory structure parallel to calendar dates.
Custom path generators are available only to storage items instantiated from Class StorageItemFilter. If you instantiate a storage item from one of the classes implementing Interface StorageItem, you specify the custom path and file name in the setPath method.
Step 1: Implement a path generator
The following snippet retrieves the current date and makes a path of the form YYYY/MM/DD/filename.png.
package upload.processors;
import com.psddev.dari.util.*;
import java.time.LocalDateTime;
public class CalendarDirectoryStorageItemPathGenerator implements StorageItemPathGenerator {
@Override
public String createPath(String fileName) {
LocalDateTime currentDate = LocalDateTime.now();
String calendarPath = currentDate.getYear() + "/" + currentDate.getMonthValue() + "/" + currentDate.getDayOfMonth();
calendarPath += "/" + fileName;
return calendarPath;
}
}
-
Creates a path string using YYYY/MM/DD format.
-
Appends to the path the passed uploaded filename.
Step 2: Configure the path generator
<Environment name="dari/storage/local/class" value="com.psddev.dari.util.LocalStorageItem" type="java.lang.String" />
<Environment name="dari/storage/local/baseUrl" value="/storage" type="java.lang.String" />
<Environment name="dari/storage/local/originBaseUrl" value="http://localhost/storage" type="java.lang.String" />
<Environment name="dari/storage/local/rootPath" value="/servers/training/www/storage/" type="java.lang.String" />
<Environment name="dari/upload/local/pathGenerator" value="upload.processors.CalendarDirectoryStorageItemPathGenerator" type="java.lang.String" />
-
Specifies the static portion of the path where Dari saves the storage item.
-
Specifies the class that generates the dynamic portion of the path where Dari saves the storage item. The value is the fully qualified class name that includes the package name.
Referring to the previous snippet, after you upload the file dragon-slayer.png, Dari saves the image in an absolute path similar to /servers/training/www/storage/2017/02/28/dragon-slayer.png.
See also: