This article describes how to use the Play framework
’s
built-in features for handling HTTP file uploads and saving the data in
files on the server’s file system. This is frequently-used in web
applications for things like allowing users to upload photos of
themselves.
Source code for this article is available at https://github.com/hilton/play-blob
.
Architectural considerations
There are generally two approaches to persisting binary data: either
save the data as a file on the server’s file system, or store the data
directly in a database table. There are pros and cons to each approach.
Using the file system may be easier to implement, but might not scale.
Using the database allows you to support transactions, but might not
scale.
Confusingly, play.db.jpa.Blob
data is stored in a file outside the database, and does not use the java.sql.Blob
type with a BLOB
in the database. On the server, Play stores the uploaded image in a file in the attachments/
folder, inside the application folder. The file name (a UUID
) and MIME type are stored in a database attribute whose SQL type is VARCHAR
.
To store the data in a database column, you would annotate a model property with @javax.persistence.Lob
which results in the data being stored in a column whose SQL type is
BLOB or CLOB. How to implement this approach and how it compares to
using the file system are beyond the scope of this article.
Upload a file and store it on the server
The basic use case of uploading, storing and serving a file is
extremely easy in Play. This is because the binding framework
automatically binds a file uploaded from an HTML form to your JPA model,
and because Play provides convenience methods that make serving binary
data as easy as serving plain text.
To store file uploads in your model, add a property of type play.db.jpa.Blob
import
play.db.jpa.Model;
|
import
javax.persistence.Entity;
|
public
class
User
extends
Model {
|
To upload files, add a form to your view template:
#{form @addUser(), enctype:'multipart/form-data'}
|
<
input
type
=
"file"
name
=
"user.photo"
>
|
<
input
type
=
"submit"
name
=
"submit"
value
=
"Upload"
>
|
Then, in the controller, add an action that saves the upload in a new model object:
public
static
void
addUser(User user) {
|
This code does not appear to do anything other than save the JPA
entity, because the file upload is handled automatically by Play. First,
before the start of the action method, the uploaded file is saved in a
sub-folder of tmp/uploads/
. Next, when the entity is saved, the file is copied to the attachments/
folder, with a UUID as the file name. Finally, when the action is complete, the temporary file is deleted.
To save attachments in a different folder, specify a different path in the application.conf
file. This can be an absolute path, or a relative path to a folder inside the Play application folder:
To display the uploaded images, add image tags to a view:
#{list items:models.User.findAll(), as:'user'}
|
<
img
src
=
"@{userPhoto(user.id)}"
>
|
Finally, add a controller method to load the model object and render the image:
public
static
void
userPhoto(
long
id) {
|
final
User user = User.findById(id);
|
response.setContentTypeIfNotSet(user.photo.type());
|
renderBinary(user.photo.get());
|
Update the upload
If you provide a user.id
form (request) parameter, you can update an entry the same way you save one:
public
static
void
updateUser(User user) {
|
If a file upload is included, this will be saved as a new file, whose
name is a new UUID. This means that the original file will now be
orphaned. If you do not have unlimited disk space then you will have to
implement your own scheme for cleaning up. There are several possible
approaches.
The brute force approach is to implement an asynchronous job
that periodically queries the JPA model for all currently-used file references, scans the attachments/
folder, and deletes unused files. This might scale badly.
Depending on your application, it might make sense to maintain
references to all previous versions of attachments in your model, so
that you can display them in the user interface, as a wiki generally
does for previous versions of each page. Then the clean-up could either
be manual, triggered when uploading a new file or from an asynchronous
job. The clean up policy might be to keep a certain number of versions,
or delete previous versions beyond a certain age. The @PreUpdate
and @PreRemove
JPA interceptors are useful for doing this kind of thing.
A more hard-core solution would be to modify play.db.jpa.Blob
to create an alternative BinaryField
field implementation that handles transaction-aware file upload updates.
Delete the upload
If you delete an object with a play.db.jpa.Blob
property, the file in the attachments/
folder is not deleted automatically. You can delete the file manually via a reference to a java.io.File
property, as in the following action method.
public
static
void
deleteUser(
long
id) {
|
final
User user = User.findById(id);
|
user.photo.getFile().delete();
|
Alternatively, you could encapsulate the file deletion in the model, by overriding the delete method in User.java
to delete the file after the database entity has been successfully deleted.
photo.getFile().delete();
|
Upload a file and save the file name
Sometimes you want to store the name of the originally uploaded file,
so that you can map the file extension to a MIME type on the server, or
so that you can serve the file as an attachment with the original file
name.
To get at the file name, you need to bind the form control to a java.io.File
action method parameter. This means that you need a new action method
in your controller that constructs the model object from separate form
parameters, instead of binding the whole model object as in the first
example.
Add the new action method to the controller, to instantiate a new model instance and its Blog
property:
public
static
void
addUserWithFileName(File photo)
throws
FileNotFoundException {
|
final
User user =
new
User();
|
user.photoFileName = photo.getName();
|
user.photo.set(
new
FileInputStream(photo), MimeTypes.getContentType(photo.getName()));
|
To make this work, first add the new photoFileName
property to the model:
public
class
User
extends
Model {
|
public
String photoFileName;
|
Next, in the template, display the saved file name with the image,
and change the form to use the new controller, and so that its file
upload control’s name is just photo
instead of user.photo
:
#{list items:models.User.findAll(), as:'user'}
|
<
img
title
=
"${user.photoFileName}"
src
=
"@{userPhoto(user.id)}"
>
|
#{form @addUserWithFileName(), enctype:'multipart/form-data'}
|
<
input
type
=
"file"
name
=
"photo"
>
|
<
input
type
=
"submit"
name
=
"submit"
value
=
"Upload"
>
|
Download the file as an attachment
When you serve a binary file to a web browser, the browser will
normally display the data in the browser window, if possible. For
example, the images in the example above are shown in-line in the
browser window if you access their URLs directly.
However, you can set an HTTP header to instruct the web browser to
treat the file as an ‘attachment’, which generally results in the web
browser downloading the file to the user’s computer.
First, add a new action for the download. Assuming that the file name is always set, the only difference to the userPhoto
action is that we pass the file name as a parameter to the renderBinary
method, which causes Play to set the Content-Disposition
response header, providing a file name.
public
static
void
downloadUserPhoto(
long
id) {
|
final
User user = User.findById(id);
|
response.setContentTypeIfNotSet(user.photo.type());
|
renderBinary(user.photo.get(), user.photoFileName);
|
Now update the list of photos in the view template to include a link to the download URL.
#{list items:models.User.findAll(), as:'user'}
|
<
a
href
=
"@{downloadUserPhoto(user.id)}"
><
img
src
=
"@{Application.userPhoto(user.id)}"
></
a
>
|
Support custom content types
The response’s content type is set in the controller’s userPhoto
action method, using the type stored in the Blob.
The play.libs.MimeTypes
looks up the MIME type for the given file name’s extension, using the list in $PLAY_HOME/framework/src/play/libs/mime-types.properties
Since Play 1.2 you can add your own types to the conf/application.conf
file. For example, to add a MIME type for GIMP images with the .xcf
extension, add the line:
mimetype.xcf=application/x-gimp-image
|
Note that with the code examples above, this only works if you use the addUserWithFileName
action above, which explicitly looks up the MIME type based on the original file name. The earlier addUser
example uses the MIME type sent in the file upload HTTP request. My web
browser (Safari 5.0.4) sets the request content type to image/png
for PNG images, but does not recognise an .xcf
file and sets its content type to application/octet-stream
.
Conclusion
The Play framework greatly simplifies the task of handling and
storing file uploads in a web application. However, this is for storing
files in the server’s file system, which might not be the ideal solution
for your application. To store the binary data in a database table, you
would need to work out how to use JPA’s @Lob
annotation.
相关推荐
Classes contained in spring-mock.jar: org.springframework.mock....org.springframework.test.jpa.OrmXmlOverridingShadowingClassLoader.class org.springframework.test.web.AbstractModelAndViewTests.class
Play Framework 是一个开源的Web应用框架,主要针对Java和Scala开发者设计,它的核心理念是简化开发流程,提高开发效率,并且特别强调了RESTful架构风格。这个“playframework中文教程.zip”压缩包很可能是为了帮助...
仓库管理系统,SpringBoot+Spring Data JPA.zip仓库管理系统,SpringBoot+Spring Data JPA.zip仓库管理系统,SpringBoot+Spring Data JPA.zip仓库管理系统,SpringBoot+Spring Data JPA.zip仓库管理系统,SpringBoot...
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/...
Spring提供了对各种持久化技术(如JDBC、Hibernate、JPA等)的支持,`org.springframework.jdbc` 和 `org.springframework.orm` 包内包含了大量的模板类和回调接口。`PlatformTransactionManager`是事务管理的核心...
Play Framework 是一个开源的Web应用框架,用于构建现代、高性能的Java和Scala应用程序。它采用模型-视图-控制器(MVC)架构模式,并且强调简洁的代码和开发的即时反馈。Play Framework API 是开发者使用该框架进行...
《Apress.Pro.JPA.2.2nd.Edition.Oct.2013》是一部关于Java持久化API(Java Persistence API,简称JPA)的专著,由Apress出版社于2013年10月出版。这本书是JPA 2.2版本的详细指南,旨在帮助开发者深入理解和有效利用...
import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository, Long> { } ``` 至此,我们已经完成了使用 Spring Boot 和 Spring Data JPA 构建基本 ...
org.springframework.transaction-3.0.0.M4.jar: 为JDBC、Hibernate、JDO、JPA等提供的一致的声明式和编程式事务管理 org.springframework.web.servlet-3.0.0.M4.jar: SpringMVC org.springframework.jms-3.0.0.M4...
首先,我们需要导入`org.springframework.data.jpa.domain.Specifications`包,并创建一个`Specification`实例。例如,如果我们有一个`User`实体类,我们想根据用户名进行查询,可以这样做: ```java public class ...
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceXmlLocation" value="classpath:persistence.xml" /> ...
通过继承`play.db.jpa.Model`类,我们可以轻松地为领域对象添加持久化支持。`Model`类提供了一个名为`id`的域,作为对象的唯一标识符,同时也是数据库表中的主键。 `play.db.jpa.JPASupport`是`Model`类的父类,...
JPA视频_PPT及源码 · 1. JPA视频_概述 · 2. JPA视频_HelloWorld · 3. JPA视频_基本注解 · 4. JPA视频_Transient注解 · 5. JPA视频_Temporal注解 · 6. JPA视频_Table主键生成策略 · 7. JPA视频_...
3.1.0.RC1版本强化了对JPA 2.0的支持,增强了事务管理功能。 4. **MVC(Model-View-Controller)**:Spring MVC是Spring提供的Web应用框架,用于构建高效、灵活的Web应用。3.1.0.RC1版本改进了模型绑定,支持多部分...
Play Framework2是一个强大的Java和Scala应用开发框架,它以其简洁的API、快速的开发周期以及对Web标准的紧密集成而闻名。本教程旨在为初学者和有经验的开发者提供全面的指导,帮助他们掌握Play Framework2的核心...
spring-jpa.jar........................................
momomo.com.platform.db.base.jpa.session.with.postgres提供与Postgres相关的实现,以实现我们与数据库相关的存储库依存关系 <artifactId>postgresql</artifactId> ... <version>42.2.19使用者您自己的应用我们提供...
在数据访问层,Spring Framework 5.0.2.RELEASE改进了对JDBC、JPA、Hibernate等ORM框架的支持。它提供了更丰富的模板类,简化了数据库操作。此外,对于NoSQL数据库,如MongoDB和Cassandra,Spring Data项目提供了...
Spring Framework是Java开发领域中最受欢迎的开源框架之一,以其模块化、松耦合和强大的功能特性,深受开发者喜爱。5.2.25.RELEASE作为Spring框架的一个稳定版本,为开发者提供了许多改进和新特性。本文将对这个版本...
1. **导入Spring库**:首先,你需要解压"spring-framework-1.2.7-with-dependencies.zip",并将解压后的spring-framework-1.2.7目录添加到JBuilder的类路径中,确保JBuilder能够找到Spring的相关类。 2. **创建项目...