Skip to content

Commit 588551c

Browse files
committed
feat(post): upload post main photo.
1 parent 4c04796 commit 588551c

File tree

12 files changed

+197
-10
lines changed

12 files changed

+197
-10
lines changed

src/main/java/config/AppWebAppInitializer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ protected String[] getServletMappings() {
3232
@Override
3333
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
3434
super.customizeRegistration(registration);
35-
registration.setMultipartConfig(new MultipartConfigElement("/tmp/blog/uploads", 2097152, 4194304, 0));
35+
registration.setMultipartConfig(new MultipartConfigElement("/", 2097152, 4194304, 0));
3636
}
3737

3838
@Override

src/main/java/controllers/admin/PostController.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@
66
package controllers.admin;
77

88
import exceptions.PostNotFoundException;
9+
import java.io.IOException;
10+
import java.sql.SQLException;
911
import java.util.ArrayList;
1012
import java.util.List;
1113
import java.util.Locale;
14+
import javax.sql.rowset.serial.SerialBlob;
1215
import javax.validation.Valid;
16+
import models.FileImage;
1317
import models.Post;
1418
import models.User;
1519
import org.slf4j.Logger;
@@ -24,6 +28,8 @@
2428
import org.springframework.web.bind.annotation.PathVariable;
2529
import org.springframework.web.bind.annotation.PostMapping;
2630
import org.springframework.web.bind.annotation.RequestMapping;
31+
import org.springframework.web.bind.annotation.RequestPart;
32+
import org.springframework.web.multipart.MultipartFile;
2733
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
2834
import services.PostService;
2935
import services.security.CurrentUser;
@@ -89,11 +95,20 @@ public String showCreatePostForm(Model model){
8995
}
9096

9197
@PostMapping("/save")
92-
public String processPost(@ModelAttribute @Valid Post post, Errors errors,
93-
@CurrentUserAttached User activeUser, RedirectAttributes model){
98+
public String processPost(@RequestPart("postImage") MultipartFile postImage, @ModelAttribute @Valid Post post, Errors errors,
99+
@CurrentUserAttached User activeUser, RedirectAttributes model) throws IOException, SQLException{
100+
94101
if(errors.hasErrors()){
95102
return "admin/post/create";
96103
}
104+
if(postImage != null){
105+
FileImage image = new FileImage();
106+
image.setName(postImage.getName());
107+
image.setContentType(postImage.getContentType());
108+
image.setSize(postImage.getSize());
109+
image.setContent(new SerialBlob(postImage.getBytes()));
110+
post.setImage(image);
111+
}
97112
post.setAuthor(activeUser);
98113
postService.create(post);
99114
List<String> successMessages = new ArrayList();

src/main/java/controllers/frontend/PostController.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,20 @@
66
package controllers.frontend;
77

88
import exceptions.PostNotFoundException;
9+
import java.sql.Blob;
10+
import java.sql.SQLException;
11+
import models.FileImage;
912
import org.slf4j.Logger;
1013
import org.slf4j.LoggerFactory;
1114
import org.springframework.beans.factory.annotation.Autowired;
15+
import org.springframework.http.MediaType;
16+
import org.springframework.http.ResponseEntity;
1217
import org.springframework.stereotype.Controller;
1318
import org.springframework.ui.Model;
1419
import org.springframework.web.bind.annotation.GetMapping;
1520
import org.springframework.web.bind.annotation.PathVariable;
1621
import org.springframework.web.bind.annotation.RequestMapping;
22+
import org.springframework.web.bind.annotation.ResponseBody;
1723
import projection.PostDetail;
1824
import services.PostService;
1925

@@ -38,4 +44,20 @@ public String show(@PathVariable Long postId, Model model){
3844
model.addAttribute("post", post);
3945
return "frontend/post/show";
4046
}
47+
48+
@GetMapping("/image/{postId}")
49+
@ResponseBody
50+
public ResponseEntity<byte[]> downloadPostImage(@PathVariable Long postId) throws SQLException {
51+
FileImage postImage = postService.getImageByPostId(postId);
52+
logger.info("Post Image Information: " + postImage.toString());
53+
Blob blob = postImage.getContent();
54+
int blobLength = (int) blob.length();
55+
byte[] blobAsBytes = blob.getBytes(1, blobLength);
56+
//release the blob and free up memory. (since JDBC 4.0)
57+
blob.free();
58+
return ResponseEntity.ok()
59+
.contentLength(postImage.getSize())
60+
.contentType(MediaType.parseMediaType(postImage.getContentType()))
61+
.body(blobAsBytes);
62+
}
4163
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* To change this license header, choose License Headers in Project Properties.
3+
* To change this template file, choose Tools | Templates
4+
* and open the template in the editor.
5+
*/
6+
package models;
7+
8+
import java.io.Serializable;
9+
import java.sql.Blob;
10+
import javax.persistence.Column;
11+
import javax.persistence.Entity;
12+
import javax.persistence.GeneratedValue;
13+
import javax.persistence.GenerationType;
14+
import javax.persistence.Id;
15+
import javax.persistence.OneToOne;
16+
import javax.persistence.Table;
17+
18+
/**
19+
*
20+
* @author sergio
21+
*/
22+
@Entity
23+
@Table(name = "images")
24+
public class FileImage implements Serializable {
25+
@Id
26+
@GeneratedValue(strategy = GenerationType.AUTO)
27+
private Long id;
28+
@Column(nullable = false)
29+
private String name;
30+
@Column(nullable = false)
31+
private String contentType;
32+
@Column(nullable = true)
33+
private Long size;
34+
@Column(nullable = false)
35+
private Blob content;
36+
@OneToOne(mappedBy = "image")
37+
private Post post;
38+
39+
public Long getId() {
40+
return id;
41+
}
42+
43+
public void setId(Long id) {
44+
this.id = id;
45+
}
46+
47+
public String getName() {
48+
return name;
49+
}
50+
51+
public void setName(String name) {
52+
this.name = name;
53+
}
54+
55+
public String getContentType() {
56+
return contentType;
57+
}
58+
59+
public void setContentType(String contentType) {
60+
this.contentType = contentType;
61+
}
62+
63+
public Long getSize() {
64+
return size;
65+
}
66+
67+
public void setSize(Long size) {
68+
this.size = size;
69+
}
70+
71+
public Blob getContent() {
72+
return content;
73+
}
74+
75+
public void setContent(Blob content) {
76+
this.content = content;
77+
}
78+
79+
80+
public Post getPost() {
81+
return post;
82+
}
83+
84+
public void setPost(Post post) {
85+
this.post = post;
86+
}
87+
88+
@Override
89+
public String toString() {
90+
return "FileImage{" + "id=" + id + ", name=" + name + ", contentType=" + contentType + ", size=" + size + '}';
91+
}
92+
}

src/main/java/models/Post.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.io.Serializable;
44
import java.util.Date;
5+
import javax.persistence.CascadeType;
56
import javax.persistence.Column;
67
import javax.persistence.Entity;
78
import javax.persistence.FetchType;
@@ -10,6 +11,7 @@
1011
import javax.persistence.Id;
1112
import javax.persistence.Lob;
1213
import javax.persistence.ManyToOne;
14+
import javax.persistence.OneToOne;
1315
import javax.persistence.Table;
1416
import javax.validation.constraints.NotNull;
1517
import javax.validation.constraints.Size;
@@ -47,18 +49,22 @@ public class Post implements Serializable {
4749
@Column(nullable = false)
4850
private Date date = new Date();
4951

52+
@OneToOne(optional = false, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
53+
private FileImage image;
54+
5055
@NotNull(message="{post.published.notnull}")
5156
@Column(nullable = false)
5257
private Boolean published = false;
5358

5459
public Post() {}
5560

56-
public Post(String title, String subtitle, String body, User author, Date date, Boolean published) {
61+
public Post(String title, String subtitle, String body, User author, Date date, FileImage image, Boolean published) {
5762
this.title = title;
5863
this.subtitle = subtitle;
5964
this.body = body;
6065
this.author = author;
6166
this.date = date;
67+
this.image = image;
6268
this.published = published;
6369
}
6470

@@ -111,6 +117,15 @@ public void setDate(Date date) {
111117
this.date = date;
112118
}
113119

120+
public FileImage getImage() {
121+
return image;
122+
}
123+
124+
public void setImage(FileImage image) {
125+
this.image = image;
126+
}
127+
128+
114129
public Boolean getPublished() {
115130
return published;
116131
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* To change this license header, choose License Headers in Project Properties.
3+
* To change this template file, choose Tools | Templates
4+
* and open the template in the editor.
5+
*/
6+
package repositories;
7+
8+
import models.FileImage;
9+
import org.springframework.data.jpa.repository.JpaRepository;
10+
11+
/**
12+
*
13+
* @author sergio
14+
*/
15+
public interface FileImageRepository extends JpaRepository<FileImage, Long>{
16+
FileImage findByPostId(Long id);
17+
}

src/main/java/services/PostService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import models.Post;
99
import java.util.List;
10+
import models.FileImage;
1011
import projection.PostByAuthor;
1112
import projection.PostDetail;
1213
import projection.PostSummary;
@@ -25,4 +26,5 @@ public interface PostService {
2526
Post edit(Post post);
2627
void delete(Post post);
2728
void delete(Long id);
29+
FileImage getImageByPostId(Long id);
2830
}

src/main/java/services/PostServiceImpl.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
package services;
77

88
import java.util.List;
9+
import models.FileImage;
910
import models.Post;
1011
import org.springframework.beans.factory.annotation.Autowired;
1112
import org.springframework.stereotype.Service;
1213
import repositories.PostRepository;
1314
import projection.PostByAuthor;
1415
import projection.PostDetail;
1516
import projection.PostSummary;
17+
import repositories.FileImageRepository;
1618

1719
/**
1820
*
@@ -23,6 +25,8 @@ public class PostServiceImpl implements PostService {
2325

2426
@Autowired
2527
private PostRepository postRepository;
28+
@Autowired
29+
private FileImageRepository fileImageRepository;
2630

2731
@Override
2832
public List<Post> findAll() {
@@ -68,4 +72,9 @@ public PostDetail findById(Long id) {
6872
public PostDetail findByIdAndPublishedTrue(Long id) {
6973
return postRepository.findByIdAndPublishedTrue(id);
7074
}
75+
76+
@Override
77+
public FileImage getImageByPostId(Long id) {
78+
return fileImageRepository.findByPostId(id);
79+
}
7180
}

src/main/webapp/WEB-INF/i18n/messages.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ form.post.body = Contenido
2727
form.post.status = Estado del Art\u00edculo
2828
form.post.published = Publicado
2929
form.post.notpublished = No Publicado
30+
form.post.image = Imagen Principal del Art\u00edculo
31+
form.post.image.label = Imagen
32+
form.post.image.help = Selecciona una imagen como cabecera del art\u00edculo.
3033
form.post.create = Crear
3134
form.post.delete = Eliminar
3235
#### Login Page ######

src/main/webapp/WEB-INF/templates/admin/fragment/post.html

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<meta charset="UTF-8">
66
</head>
77
<body>
8-
<form th:fragment="form(target, delete)" id="postForm" name="postForm" method="post" action="#" th:action="${target}" th:object="${post}">
8+
<form th:fragment="form(target, delete)" enctype="multipart/form-data" id="postForm" name="postForm" method="post" action="#" th:action="${target}" th:object="${post}">
99
<div class="alert alert-danger" th:if="${#fields.hasErrors('*')}">
1010
<ul>
1111
<li th:each="err: ${#fields.errors('*')}" th:text="${err}">Input is incorrect</li>
@@ -27,17 +27,29 @@
2727
</div>
2828
<div class="row control-group">
2929
<div class="form-group col-xs-12">
30-
<label class="col-md-2 control-label" th:text="#{form.post.status}">Post status</label>
31-
<div class="radio radio-inline">
30+
<label th:text="#{form.post.status}">Post status</label>
31+
<div class="radio col-xs-12">
3232
<label for="radio_published" th:text="#{form.post.published}">Published</label>
3333
<input th:field="*{published}" type="radio" id="radio_published" name="post_status" value="true" />
3434
</div>
35-
<div class="radio radio-inline">
35+
<div class="radio col-xs-12">
3636
<label for="radio_not_published" th:text="#{form.post.notpublished}">Not Published</label>
3737
<input th:field="*{published}" type="radio" id="radio_not_published" name="post_status" value="false" />
3838
</div>
3939
</div>
4040
</div>
41+
<div class="row control-group">
42+
<legend th:text="#{form.post.image}">Post Image</legend>
43+
<div class="form-group">
44+
<label class="col-md-2 control-label" th:text="#{form.post.image.label}">Image</label>
45+
<div class="col-md-10">
46+
<input type="file" accept="image/jpeg,image/png,image/gif" class="btn btn-default" id="image" name="postImage">
47+
<p class="help-block" th:text="#{form.post.image.help}">
48+
some help text here.
49+
</p>
50+
</div>
51+
</div>
52+
</div>
4153
<div class="row control-group">
4254
<div class="form-group col-xs-12">
4355
<label th:text="#{form.post.body}">Body</label>

0 commit comments

Comments
 (0)