package com.example.demo.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@Controller
public class FileDownloadController {
@Value("${file.upload-dir}")
private String uploadDir;
@GetMapping("/download")
public ResponseEntity<InputStreamResource> downloadFile(@RequestParam String fileName) throws IOException {
// 파일 경로
String filePath = uploadDir + fileName;
File file = new File(filePath);
if (!file.exists()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
// 파일을 InputStreamResource로 감싸기
InputStreamResource resource = new InputStreamResource(new FileInputStream(file));
// 파일의 MIME 타입 결정 (기본적으로 바이너리로 설정)
String contentType = "application/octet-stream";
try {
contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
} catch (Exception ex) {
ex.printStackTrace();
}
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + file.getName())
.contentType(MediaType.parseMediaType(contentType))
.contentLength(file.length())
.body(resource);
}
}
자바로 ResponseEntity 는 RESTful한 것을 작성하고 위해 사용하는데 객체와 응답코드를 같이 보내줄수 있다.
타임리프와 스프링 커스텀태그가 유사한 부분이 있는데 뭐 어쨌건 이미 잘 짜여진 타임리프를 쓰는게 나은 이유인것 같다. 유지보수 측면이랑 디버깅 측면을 고려하면 말이다.
수타게 보아온 엑셀은 poi 패키지 가져와서 Workbook , Sheet 클래스를 중점으로 구성하여
ByteArrayOutputStream 을 통해 보내면 되는 거를 새록새록 떠올려본다.
package com.example.demo.controller;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@Controller
public class ExcelController {
@GetMapping("/download/excel")
public ResponseEntity<byte[]> downloadExcel() throws IOException {
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet1");
// 헤더 행 생성
Row headerRow = sheet.createRow(0);
Cell headerCell1 = headerRow.createCell(0);
headerCell1.setCellValue("ID");
Cell headerCell2 = headerRow.createCell(1);
headerCell2.setCellValue("Name");
// 데이터 행 생성
Row dataRow = sheet.createRow(1);
Cell dataCell1 = dataRow.createCell(0);
dataCell1.setCellValue(1);
Cell dataCell2 = dataRow.createCell(1);
dataCell2.setCellValue("John Doe");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
workbook.write(outputStream);
workbook.close();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", "sample.xlsx");
return ResponseEntity
.ok()
.headers(headers)
.body(outputStream.toByteArray());
}
}
pdf 는 iText 가져와서 똑같이 ByteArrayOutputStream 을 통해 보내면 되는 것 또한 새록새록이다.
PdfWriter/ Document 냐 WorkBook/Sheet이냐 차이가 좀 있지만 말이다.
package com.example.demo.controller;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@Controller
public class PdfController {
@GetMapping("/download/pdf")
public ResponseEntity<byte[]> downloadPdf() throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PdfWriter writer = new PdfWriter(outputStream);
Document document = new Document(new com.itextpdf.kernel.pdf.PdfDocument(writer));
document.add(new Paragraph("Hello, PDF!"));
document.close();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_PDF);
headers.setContentDispositionFormData("attachment", "sample.pdf");
return ResponseEntity
.ok()
.headers(headers)
.body(outputStream.toByteArray());
}
}
http통신에만 익숙해 왔는데 socket 양방향 통신도 다시 한번 살펴본다.
websockt가 config 설정을 하고
메시지 브로커와 목적지 prefixes를 하고나서
endpoint 설정을 하면 된다.
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic"); // 메시지 브로커 설정
config.setApplicationDestinationPrefixes("/app"); // 애플리케이션 목적지 프리픽스 설정
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS(); // 웹소켓 엔드포인트 설정
}
}
이것을 다루기 위해서 메시지 핸들러가 필요한데, 클라이언트로부터 메세지를 받고, 브로커를 통해 다른 클라이언트에 메시지를 전달하는 역할을 한다. 양방향 통신이 되는 순간이다.
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
@Controller
public class ChatController {
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
Thread.sleep(1000); // simulated delay
return new Greeting("Hello, " + message.getName() + "!");
}
}
프론트에서 이렇게 소켓 만들어서 위에 설정한 ws 엔드포인트로 보낼 준비하고, @MessageMapping(app/hello) 어노테이션을 통한 메세지 핸들러로 전달하면 브로커가 (/topic) @SendTo 어노테이션을 통해(/grettings)다른 클라이언트에게 전달한다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebSocket Chat</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.1.4/sockjs.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>
<script>
var stompClient = null;
function connect() {
var socket = new SockJS('/ws');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/greetings', function (greeting) {
showGreeting(JSON.parse(greeting.body).content);
});
});
}
function disconnect() {
if (stompClient !== null) {
stompClient.disconnect();
}
console.log("Disconnected");
}
function sendName() {
var name = document.getElementById('name').value;
stompClient.send("/app/hello", {}, JSON.stringify({'name': name}));
}
function showGreeting(message) {
var response = document.getElementById('response');
var p = document.createElement('p');
p.appendChild(document.createTextNode(message));
response.appendChild(p);
}
</script>
</head>
<body>
<div>
<input type="text" id="name" placeholder="Enter your name" />
<button onclick="sendName()">Send</button>
<button onclick="connect()">Connect</button>
<button onclick="disconnect()">Disconnect</button>
</div>
<div id="response"></div>
</body>
</html>
어쨌건 또 중요포인트로 JSON은 parse 해서 객체로 만들고 stringify로 문자열로 만들고
{
"name": "John",
"age": 30,
"isStudent": false,
"address": {
"street": "123 Main St",
"city": "Anytown"
},
"hobbies": ["reading", "traveling", "swimming"]
}
날자 타입이 지원안되서 그렇지 상당히 자주 사용되고 유용한 데이터 타입이다. 배열[] 과 함께 쓸수 있어서 더좋다.
'4차산업혁명의 일꾼 > Java&Spring웹개발과 서버 컴퓨터' 카테고리의 다른 글
OAuth와 SSR (2) | 2024.06.18 |
---|---|
log와 junit5 ,validation 그리고 interceptor (2) | 2024.06.17 |
javascript/jsp및 프론트 복습 (0) | 2024.06.17 |
웹개발의 추억을 반추하며 스프링 MVC 기초공사 복습과 스프링 Security (0) | 2024.06.17 |
컴퓨터를 운영하는 시스템 소프트웨어 운영체제 (2) | 2024.06.06 |