`
thierry.xing
  • 浏览: 666185 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
580fa9c1-4a0c-3f40-a55a-c9256ce73302
Sencha Touch中...
浏览量:0
社区版块
存档分类
最新评论

Play framework file upload with play.db.jpa.Blob

    博客分类:
  • Java
 
阅读更多

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

package models;
import play.db.jpa.Blob;
import play.db.jpa.Model;
import javax.persistence.Entity;
@Entity
public class User extends Model {
    public String name;
    public Blob photo;
}

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" >
#{/form}

Then, in the controller, add an action that saves the upload in a new model object:

public static void addUser(User user) {
    user.save();
    index();
}

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:

attachments.path=photos

To display the uploaded images, add image tags to a view:

#{list items:models.User.findAll(), as:'user'}
    < img src = "@{userPhoto(user.id)}" >
#{/list}

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);
    notFoundIfNull(user);
    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) {
    user.save();
    index();
}

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();
    user.delete();
    index();
}

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.

@Override
public void _delete() {
    super ._delete();
    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 = new Blob();
    user.photo.set( new FileInputStream(photo), MimeTypes.getContentType(photo.getName()));
    user.save();
    index();
}

To make this work, first add the new photoFileName property to the model:

@Entity
public class User extends Model {
    public String photoFileName;
    public Blob photo;
}

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)}" >
#{/list}
#{form @addUserWithFileName(), enctype:'multipart/form-data'}
    < input type = "file" name = "photo" >
    < input type = "submit" name = "submit" value = "Upload" >
#{/form}

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);
    notFoundIfNull(user);
    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 >
#{/list}

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.

分享到:
评论

相关推荐

    spring-mock.jar

    Classes contained in spring-mock.jar: org.springframework.mock....org.springframework.test.jpa.OrmXmlOverridingShadowingClassLoader.class org.springframework.test.web.AbstractModelAndViewTests.class

    playframework中文教程.zip

    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+Spring Data JPA.zip仓库管理系统,SpringBoot...

    解决SpringDataJPA报错:org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null w

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/...

    lijie_study-spring-framework-v5.2.0.RELEASE.zip

    Spring提供了对各种持久化技术(如JDBC、Hibernate、JPA等)的支持,`org.springframework.jdbc` 和 `org.springframework.orm` 包内包含了大量的模板类和回调接口。`PlatformTransactionManager`是事务管理的核心...

    play framework api,play! framework api,play api

    Play Framework 是一个开源的Web应用框架,用于构建现代、高性能的Java和Scala应用程序。它采用模型-视图-控制器(MVC)架构模式,并且强调简洁的代码和开发的即时反馈。Play Framework API 是开发者使用该框架进行...

    Apress.Pro.JPA.2.2nd.Edition.Oct.2013

    《Apress.Pro.JPA.2.2nd.Edition.Oct.2013》是一部关于Java持久化API(Java Persistence API,简称JPA)的专著,由Apress出版社于2013年10月出版。这本书是JPA 2.2版本的详细指南,旨在帮助开发者深入理解和有效利用...

    springBoot+springjpa.docx

    import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository, Long&gt; { } ``` 至此,我们已经完成了使用 Spring Boot 和 Spring Data JPA 构建基本 ...

    spring-framework-3.0.0.M4-with-docs

    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...

    spring data jpa 的Specifications动态查询

    首先,我们需要导入`org.springframework.data.jpa.domain.Specifications`包,并创建一个`Specification`实例。例如,如果我们有一个`User`实体类,我们想根据用户名进行查询,可以这样做: ```java public class ...

    Spring和openJPA集成

    &lt;bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"&gt; &lt;property name="persistenceXmlLocation" value="classpath:persistence.xml" /&gt; ...

    play framework db

    通过继承`play.db.jpa.Model`类,我们可以轻松地为领域对象添加持久化支持。`Model`类提供了一个名为`id`的域,作为对象的唯一标识符,同时也是数据库表中的主键。 `play.db.jpa.JPASupport`是`Model`类的父类,...

    尚硅谷JPA视频教程

    JPA视频_PPT及源码 · 1. JPA视频_概述 · 2. JPA视频_HelloWorld · 3. JPA视频_基本注解 · 4. JPA视频_Transient注解 · 5. JPA视频_Temporal注解 · 6. JPA视频_Table主键生成策略 · 7. JPA视频_...

    spring-framework-3.1.0.RC1-with-docs.zip

    3.1.0.RC1版本强化了对JPA 2.0的支持,增强了事务管理功能。 4. **MVC(Model-View-Controller)**:Spring MVC是Spring提供的Web应用框架,用于构建高效、灵活的Web应用。3.1.0.RC1版本改进了模型绑定,支持多部分...

    Play Framework2本教程

    Play Framework2是一个强大的Java和Scala应用开发框架,它以其简洁的API、快速的开发周期以及对Web标准的紧密集成而闻名。本教程旨在为初学者和有经验的开发者提供全面的指导,帮助他们掌握Play Framework2的核心...

    spring-jpa.jar

    spring-jpa.jar........................................

    momomo.com.platform.db.base.jpa.session.with.postgres

    momomo.com.platform.db.base.jpa.session.with.postgres提供与Postgres相关的实现,以实现我们与数据库相关的存储库依存关系 &lt;artifactId&gt;postgresql&lt;/artifactId&gt; ... &lt;version&gt;42.2.19使用者您自己的应用我们提供...

    spring-framework-5.0.2.RELEASE-dist.zip

    在数据访问层,Spring Framework 5.0.2.RELEASE改进了对JDBC、JPA、Hibernate等ORM框架的支持。它提供了更丰富的模板类,简化了数据库操作。此外,对于NoSQL数据库,如MongoDB和Cassandra,Spring Data项目提供了...

    spring-framework-5.2.25.RELEASE.tar.gz

    Spring Framework是Java开发领域中最受欢迎的开源框架之一,以其模块化、松耦合和强大的功能特性,深受开发者喜爱。5.2.25.RELEASE作为Spring框架的一个稳定版本,为开发者提供了许多改进和新特性。本文将对这个版本...

    spring-framework-1.2.7-with-dependencies.zip

    1. **导入Spring库**:首先,你需要解压"spring-framework-1.2.7-with-dependencies.zip",并将解压后的spring-framework-1.2.7目录添加到JBuilder的类路径中,确保JBuilder能够找到Spring的相关类。 2. **创建项目...

Global site tag (gtag.js) - Google Analytics