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
仓库管理系统,SpringBoot+Spring Data JPA.zip仓库管理系统,SpringBoot+Spring Data JPA.zip仓库管理系统,SpringBoot+Spring Data JPA.zip仓库管理系统,SpringBoot+Spring Data JPA.zip仓库管理系统,SpringBoot...
Apress.Pro.JPA.2.2nd.Edition.Oct.2013
JPA视频_PPT及源码 · 1. JPA视频_概述 · 2. JPA视频_HelloWorld · 3. JPA视频_基本注解 · 4. JPA视频_Transient注解 · 5. JPA视频_Temporal注解 · 6. JPA视频_Table主键生成策略 · 7. JPA视频_...
JPA批注参考.JPA批注参考.JPA批注参考.JPA批注参考.
JPA注解.doc JPA注解.doc JPA注解.doc JPA注解.doc JPA注解.doc JPA注解.doc JPA注解.doc JPA注解.doc JPA注解.doc JPA注解.doc JPA注解.doc JPA注解.doc JPA注解.doc JPA注解.doc JPA注解.doc JPA注解.doc JPA注解....
spring-jpa.jar........................................
momomo.com.platform.db.base.jpa.session.with.postgres提供与Postgres相关的实现,以实现我们与数据库相关的存储库依存关系 <artifactId>postgresql</artifactId> ... <version>42.2.19使用者您自己的应用我们提供...
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...
用play.db.jpa.Blob存储上传文件 - 74 - 强制保存 - 75 - 更多公共类型generic typing问题 - 77 - 08.Play.libs库包 - 78 - 用XPath解析XML - 78 - Web Service client - 79 - Functional programming with Java功能...
Apress.Pro.JPA.2.in.Java.EE.8.An.In-Depth.Guide.to.Java.Persistence.APIs.3rd.Edition.
hibernate-jpa.jar
org.hibernate.jpa.QueryHints jar hibernate-entitymanager-4.3.0.Final.jar
momomo.com.platform.db.base.jpa.session 与数据库相关的基于hibernate会话的jpa模块的绝对基础 依存关系 使用者 在Maven 上可用的Maven依赖项 相依性 < groupId>com.momomo</ groupId> < artifactId>momomo....
jpa--15.jpa基本apijpa--15.jpa基本apijpa--15.jpa基本apijpa--15.jpa基本apijpa--15.jpa基本apijpa--15.jpa基本api
jar包,官方版本,自测可用
jar包,官方版本,自测可用
个人博客系统(SpringBoot+jpa).zip个人博客系统(SpringBoot+jpa).zip个人博客系统(SpringBoot+jpa).zip个人博客系统(SpringBoot+jpa).zip个人博客系统(SpringBoot+jpa).zip个人博客系统(SpringBoot+jpa)...
NULL 博文链接:https://abstractforever.iteye.com/blog/642219
spring-data-simpledb.zip,根据spring数据接口提供以pojo为中心的模型以与amazon simpledb交互,非关系数据存储根据spring数据接口提供以pojo为中心的模型以与amazonsimpledb(非关系数据存储)交互