Skip to content
Snippets Groups Projects
Commit 225509dd authored by Richard Githuba's avatar Richard Githuba
Browse files

uploading pictures now works

parent 101101da
No related branches found
No related tags found
No related merge requests found
Showing
with 140 additions and 25 deletions
package polish_community_group_11.polish_community.feed;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Value("${file.upload-dir}")
private String uploadDir;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/uploads/**")
.addResourceLocations("file:" + uploadDir + "/");
}
}
\ No newline at end of file
......@@ -8,9 +8,11 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import polish_community_group_11.polish_community.feed.models.FeedImpl;
import polish_community_group_11.polish_community.feed.repository.FeedRepository;
import polish_community_group_11.polish_community.feed.services.FeedService;
import polish_community_group_11.polish_community.feed.services.FileStorageService;
import polish_community_group_11.polish_community.register.models.User;
import polish_community_group_11.polish_community.register.services.UserService;
......@@ -23,14 +25,16 @@ public class FeedApisController {
private final UserService userService;
private final FeedService feedService;
private final FileStorageService fileStorageService;
private static final Logger log = LoggerFactory.getLogger(FeedApisController.class);
public FeedApisController(FeedRepository feedRepository, FeedService feedService, UserService userService) {
public FeedApisController(FeedRepository feedRepository, FeedService feedService, UserService userService, FileStorageService fileStorageService) {
this.feedService = feedService;
this.feedRepository = feedRepository;
this.userService = userService;
this.fileStorageService = fileStorageService;
}
// getting all posts
......@@ -80,18 +84,27 @@ public class FeedApisController {
}
// creating a new post
@PostMapping("/add")
public ResponseEntity<?> addNewPost(@RequestBody FeedImpl feed) {
public ResponseEntity<?> addNewPost(
@RequestPart("post") FeedImpl feed,
@RequestPart(value = "image", required = false) MultipartFile image) {
try {
if (image != null && !image.isEmpty()) {
String imageUrl = fileStorageService.storeFile(image);
feed.setPostImageUrl(imageUrl);
}
feedService.addNewFeedPost(feed);
return ResponseEntity.ok().body("Post created successfully");
} catch (SecurityException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("User not authenticated");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error creating post: " + e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Error creating post: " + e.getMessage());
}
}
// updating the post
@PatchMapping("/{postId}")
public ResponseEntity<Void> updatePost(@PathVariable int postId, @RequestBody FeedImpl feed) {
......
package polish_community_group_11.polish_community.feed.services;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
@Service
public class FileStorageService {
@Value("${file.upload-dir:uploads}")
private String uploadDir;
private Path root;
@PostConstruct
public void init() throws IOException {
this.root = Paths.get(uploadDir);
if (!Files.exists(root)) {
Files.createDirectories(root);
}
}
public String storeFile(MultipartFile file) throws IOException {
// making unique file name
String filename = UUID.randomUUID().toString() + "_" + file.getOriginalFilename();
// full path of file
Path filePath = this.root.resolve(filename);
// save file
Files.copy(file.getInputStream(), filePath);
// relative path usable in urls
return "/uploads/" + filename;
}
public void deleteFile(String filename) throws IOException {
Path filePath = this.root.resolve(filename);
Files.deleteIfExists(filePath);
}
}
\ No newline at end of file
package polish_community_group_11.polish_community.security;
import lombok.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
......@@ -14,6 +15,8 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import polish_community_group_11.polish_community.register.models.User;
import polish_community_group_11.polish_community.register.services.UserService;
import polish_community_group_11.polish_community.register.models.Role;
......@@ -117,4 +120,6 @@ public class WebSecurityConfig {
};
}
}
......@@ -22,6 +22,9 @@ spring.sql.init.data-locations=classpath:/data.sql, classpath:/data_information_
#spring.jpa.hibernate.ddl-auto=none
spring.jpa.defer-datasource-initialization=true
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
file.upload-dir=uploads
......@@ -153,28 +153,33 @@ async function handleLike(postId, likeCountElement) {
postForm.addEventListener('submit', async (event) => {
event.preventDefault();
// getting form data
const postTitle = document.getElementById('postTitle').value;
const postDescription = document.getElementById('postDescription').value;
const postTagsInput = document.getElementById('postTags').value;
// convert comma-separated tags to array and trim whitespace
const tags = postTagsInput
.split(',')
.map(tag => tag.trim())
.filter(tag => tag.length > 0);
const data = {
postTitle,
postDescription,
tags
const formData = new FormData();
// getting post data
const postData = {
postTitle: document.getElementById('postTitle').value,
postDescription: document.getElementById('postDescription').value,
tags: document.getElementById('postTags').value
.split(',')
.map(tag => tag.trim())
.filter(tag => tag.length > 0)
};
formData.append('post', new Blob([JSON.stringify(postData)], {
type: 'application/json'
}));
// handle the case of an image selected
const imageFile = document.getElementById('postImage').files[0];
if (imageFile) {
formData.append('image', imageFile);
}
try {
let url = `${API_BASE_URL}/add`;
let method = 'POST';
// if it is edit change endpoint
if (isEditing && editPostId) {
url = `${API_BASE_URL}/${editPostId}`;
method = 'PATCH';
......@@ -182,11 +187,8 @@ postForm.addEventListener('submit', async (event) => {
const response = await fetch(url, {
method: method,
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify(data)
body: formData
});
if (!response.ok) {
......@@ -203,6 +205,22 @@ postForm.addEventListener('submit', async (event) => {
}
});
//previewing the image selected
document.getElementById('postImage').addEventListener('change', function(e) {
const file = e.target.files[0];
const preview = document.getElementById('imagePreview');
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
preview.innerHTML = `<img src="${e.target.result}" style="max-width: 200px; max-height: 200px;">`;
}
reader.readAsDataURL(file);
} else {
preview.innerHTML = '';
}
});
// onclick handling for edit
document.addEventListener('click', async (event) => {
const editButton = event.target.closest('.edit-post');
......
......@@ -100,6 +100,15 @@
required></textarea>
</label>
<label for="postImage">Image
<input type="file"
id="postImage"
name="postImage"
accept="image/*"
class="form-control">
<div id="imagePreview" class="mt-2"></div>
</label>
<label for="postTags">Tags
<input type="text" id="postTags" name="postTags"
placeholder="Enter tags separated by commas (e.g., news, community, event)"
......
uploads/36b2c38d-c9d5-4e14-a433-895a565d3abf_steve-johnson-D7AuHpLxLPA-unsplash.jpg

1.77 MiB

File added
uploads/d0753820-30b3-429c-92c4-d82c910ba083_nicolas-jehly-0UU9-_1EMvM-unsplash.jpg

3.17 MiB

0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment