Skip to content

Commit 470ee8a

Browse files
committed
Add Base64 file upload functionality and related utilities
Introduced a new endpoint to handle Base64 PDF file uploads. Added `Base64FileUploadRequest`, utility conversion methods, and a custom `MultipartFile` implementation. Updated configurations and dependencies for broader compatibility.
1 parent a1796ad commit 470ee8a

File tree

8 files changed

+159
-7
lines changed

8 files changed

+159
-7
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ repositories {
2525
}
2626

2727
ext {
28-
set('springAiVersion', "1.0.0-M5")
28+
set('springAiVersion', "1.0.0-M6")
2929
}
3030

3131
dependencies {

src/main/java/app/quantun/summary/config/ai/AiConfig.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,11 @@ ChatClient anthropicChatClient(AnthropicChatModel chatModel) {
5050
@Bean
5151
public ChatClient perplexityChatClient(@Qualifier("perplexityProperties")
5252
PerplexityProperties perplexityProperties) {
53-
OpenAiApi openAiApi = new OpenAiApi(perplexityProperties.getBaseUrl(), perplexityProperties.getApiKey());
53+
OpenAiApi openAiApi = new OpenAiApi(perplexityProperties.getBaseUrl()+perplexityProperties.getChat().getCompletionsPath(), perplexityProperties.getApiKey());
54+
5455
OpenAiChatOptions chatOptions = OpenAiChatOptions.builder()
5556
.model(perplexityProperties.getChat().getModel())
57+
5658
.temperature(perplexityProperties.getChat().getTemperature())
5759
.build();
5860
OpenAiChatModel chatModel = new OpenAiChatModel(openAiApi, chatOptions);

src/main/java/app/quantun/summary/config/serializer/JacksonConfig.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,17 @@
88
import org.springframework.context.annotation.Bean;
99
import org.springframework.context.annotation.Configuration;
1010

11+
12+
1113
@Configuration
1214
public class JacksonConfig {
1315
@Bean
1416
public ObjectMapper objectMapper() {
1517
ObjectMapper mapper = new ObjectMapper();
1618
// Optionally, ignore unknown properties globally
1719
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
20+
21+
1822
// Register the mixin for UnknownFieldSet
1923
mapper.addMixIn(UnknownFieldSet.class, UnknownFieldSetMixin.class);
2024
return mapper;

src/main/java/app/quantun/summary/controller/PdfController.java

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package app.quantun.summary.controller;
22

33
import app.quantun.summary.model.contract.dto.TableIndexContent;
4+
import app.quantun.summary.model.contract.request.Base64FileUploadRequest;
45
import app.quantun.summary.service.PdfServices;
6+
import app.quantun.summary.util.Util;
57
import io.swagger.v3.oas.annotations.Operation;
68
import io.swagger.v3.oas.annotations.media.Content;
79
import io.swagger.v3.oas.annotations.media.Schema;
@@ -14,12 +16,11 @@
1416
import org.springframework.beans.factory.annotation.Qualifier;
1517
import org.springframework.http.MediaType;
1618
import org.springframework.http.ResponseEntity;
17-
import org.springframework.web.bind.annotation.GetMapping;
18-
import org.springframework.web.bind.annotation.PostMapping;
19-
import org.springframework.web.bind.annotation.RequestParam;
20-
import org.springframework.web.bind.annotation.RestController;
19+
import org.springframework.web.bind.annotation.*;
2120
import org.springframework.web.multipart.MultipartFile;
2221

22+
import java.io.IOException;
23+
2324
@RestController
2425
@RequiredArgsConstructor
2526
@Tag(name = "File Upload", description = "Endpoints for PDF file uploads")
@@ -68,6 +69,54 @@ public ResponseEntity<String> handleFileUpload(@RequestParam("file") MultipartFi
6869
return ResponseEntity.ok("File uploaded successfully: " + fileName);
6970
}
7071

72+
73+
74+
75+
76+
77+
78+
79+
80+
81+
82+
@PostMapping(value = "/upload-base64", consumes = MediaType.APPLICATION_JSON_VALUE)
83+
@Operation(
84+
summary = "Upload PDF file",
85+
description = "Upload a PDF file to the server",
86+
responses = {
87+
@ApiResponse(
88+
responseCode = "200",
89+
description = "File uploaded successfully",
90+
content = @Content(schema = @Schema(implementation = String.class))),
91+
@ApiResponse(
92+
responseCode = "400",
93+
description = "Invalid file format or empty file",
94+
content = @Content),
95+
@ApiResponse(
96+
responseCode = "500",
97+
description = "Internal server error",
98+
content = @Content)
99+
})
100+
public ResponseEntity<String> handleFileUpload(@RequestBody Base64FileUploadRequest file) {
101+
if (file == null) {
102+
return ResponseEntity.badRequest().body("File cannot be null");
103+
}
104+
String fileName = null;
105+
try {
106+
fileName = this.pdfServices.storePdfFile(Util.base64ToMultipart(file));
107+
} catch (IOException e) {
108+
return ResponseEntity.badRequest().body("File cannot be null");
109+
}
110+
return ResponseEntity.ok("File uploaded successfully: " + fileName);
111+
112+
113+
}
114+
115+
116+
117+
118+
119+
71120
@PostMapping("/get-table-of-content")
72121
public TableIndexContent getTableOfContent(String message) {
73122
return this.pdfServices.getBookTableOfContentPages(message);
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package app.quantun.summary.model.contract.dto;
2+
3+
import org.springframework.web.multipart.MultipartFile;
4+
5+
import java.io.ByteArrayInputStream;
6+
import java.io.File;
7+
import java.io.IOException;
8+
import java.io.InputStream;
9+
import java.nio.file.Files;
10+
11+
public class CustomMultipartFile implements MultipartFile {
12+
private final byte[] content;
13+
private final String name;
14+
private final String contentType;
15+
16+
public CustomMultipartFile(byte[] content, String name, String contentType) {
17+
this.content = content;
18+
this.name = name;
19+
this.contentType = contentType;
20+
}
21+
22+
@Override
23+
public String getName() { return name; }
24+
25+
@Override
26+
public String getOriginalFilename() { return name; }
27+
28+
@Override
29+
public String getContentType() { return contentType; }
30+
31+
@Override
32+
public boolean isEmpty() { return content.length == 0; }
33+
34+
@Override
35+
public long getSize() { return content.length; }
36+
37+
@Override
38+
public byte[] getBytes() { return content; }
39+
40+
@Override
41+
public InputStream getInputStream() {
42+
return new ByteArrayInputStream(content);
43+
}
44+
45+
@Override
46+
public void transferTo(File dest) throws IOException {
47+
Files.write(dest.toPath(), content);
48+
}
49+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package app.quantun.summary.model.contract.request;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Data;
5+
import lombok.NoArgsConstructor;
6+
7+
@Data
8+
@NoArgsConstructor
9+
@AllArgsConstructor
10+
public class Base64FileUploadRequest {
11+
private String base64File;
12+
private String fileName;
13+
}
14+

src/main/java/app/quantun/summary/util/Util.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
package app.quantun.summary.util;
22

3+
import app.quantun.summary.model.contract.dto.CustomMultipartFile;
34
import app.quantun.summary.model.contract.dto.SsmlSpeak;
5+
import app.quantun.summary.model.contract.request.Base64FileUploadRequest;
46
import jakarta.xml.bind.JAXBContext;
57
import jakarta.xml.bind.JAXBException;
68
import jakarta.xml.bind.Marshaller;
79
import lombok.extern.slf4j.Slf4j;
10+
import org.springframework.web.multipart.MultipartFile;
811

12+
import java.io.IOException;
913
import java.io.StringWriter;
14+
import java.nio.file.Files;
15+
import java.nio.file.Path;
16+
import java.util.Base64;
17+
1018

1119
/**
1220
* Utility class for SSML (Speech Synthesis Markup Language) operations.
@@ -36,4 +44,30 @@ public static String toSsml(SsmlSpeak speak) {
3644
}
3745
return "";
3846
}
47+
48+
public static MultipartFile base64ToMultipart(Base64FileUploadRequest file) throws IOException {
49+
// Extract metadata and decode
50+
51+
String data = file.getBase64File();
52+
String mimeType = "application/pdf";
53+
byte[] bytes = Base64.getDecoder().decode(data);
54+
55+
// Create temp file
56+
Path tempFile = Files.createTempFile("file", ".tmp");
57+
Files.write(tempFile, bytes);
58+
59+
// Generate filename with extension
60+
String extension = switch (mimeType) {
61+
case "image/png" -> ".png";
62+
case "image/jpeg" -> ".jpg";
63+
case "application/pdf" -> ".pdf";
64+
default -> throw new IllegalArgumentException("Unsupported MIME type");
65+
};
66+
67+
return new CustomMultipartFile(
68+
bytes,
69+
file.getFileName() + extension,
70+
mimeType
71+
);
72+
}
3973
}

src/main/resources/application.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ spring.ai.vertex.ai.gemini.location=us-central1
6060
spring.ai.vertex.ai.gemini.project-id=${GEMINI_PROJECT_ID}
6161
# Chat options for Google Gemini
6262
spring.ai.google.gemini.chat.options.temperature=1.0
63-
spring.ai.google.gemini.chat.options.top-p=0.95
63+
spring.ai.google.gemini.chat.options.top-p=0.75
6464
spring.ai.google.gemini.chat.options.top-k=40
6565
spring.ai.google.gemini.chat.options.max_output_tokens=8192
6666
############################################################

0 commit comments

Comments
 (0)