Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#225] 코드 리팩토링 #230

Merged
merged 11 commits into from
Sep 5, 2024
121 changes: 98 additions & 23 deletions src/main/java/com/speech/up/api/converter/WavToRaw.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
package com.speech.up.api.converter;

import javax.sound.sampled.*;
import java.io.*;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Optional;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;

import org.springframework.web.multipart.MultipartFile;

import jakarta.validation.constraints.NotNull;
Expand All @@ -20,36 +28,103 @@ public WavToRaw() {
super();
}

// WAV 파일을 읽어 PCM 데이터를 바이트 배열로 반환
public byte[] convertToRawPcm(MultipartFile multipartFile) throws UnsupportedAudioFileException, IOException {
File file = File.createTempFile("uploaded-", ".wav");
// MultipartFile 데이터를 임시 파일로 저장
multipartFile.transferTo(file);
try (AudioInputStream sourceStream = AudioSystem.getAudioInputStream(file)) {
AudioFormat sourceFormat = sourceStream.getFormat();
AudioFormat targetFormat = FORMAT;
// WAV 파일을 읽어 PCM 데이터를 Optional<byte[]>로 반환
public Optional<byte[]> convertToRawPcm(MultipartFile multipartFile) {
Optional<File> tempFile = createTempFile(multipartFile);
if (tempFile.isEmpty()) {
return Optional.empty(); // 파일 생성 실패 시 빈 Optional 반환
}

if (!AudioSystem.isConversionSupported(targetFormat, sourceFormat)) {
throw new UnsupportedAudioFileException("The source format is not supported.");
}
Optional<byte[]> pcmData = processAudioFile(tempFile.get());
deleteTempFile(tempFile.get());

try (AudioInputStream convertedStream = AudioSystem.getAudioInputStream(targetFormat, sourceStream);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
return pcmData;
}

byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = convertedStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, bytesRead);
}
// MultipartFile을 임시 파일로 변환
private Optional<File> createTempFile(MultipartFile multipartFile) {
try {
File tempFile = File.createTempFile("uploaded-", ".wav");
multipartFile.transferTo(tempFile); // MultipartFile을 임시 파일로 저장
return Optional.of(tempFile);
} catch (IOException e) {
log.error("임시 파일 생성 실패: {}", e.getMessage());
return Optional.empty(); // 파일 생성 실패 시 빈 Optional 반환
}
}

byte[] rawPcmData = byteArrayOutputStream.toByteArray();
return formatWav2Raw(rawPcmData);
// 임시 파일 삭제
private void deleteTempFile(File tempFile) {
if (!fileExists(tempFile)) {
return; // 파일이 존재하지 않으면 조기 리턴
}

if (!tempFile.delete()) {
log.warn("임시 파일 삭제 실패: {}", tempFile.getPath());
}
}

// 파일 존재 여부 확인
private boolean fileExists(File file) {
return file != null && file.exists();
}

// 파일을 처리하여 PCM 데이터로 변환
private Optional<byte[]> processAudioFile(File file) {
Optional<AudioInputStream> sourceStream = getAudioInputStream(file);
if (sourceStream.isEmpty()) {
return Optional.empty(); // AudioInputStream 생성 실패 시 빈 Optional 반환
}

Optional<AudioInputStream> convertedStream = convertAudioStream(sourceStream.get());
if (convertedStream.isEmpty()) {
return Optional.empty(); // AudioStream 변환 실패 시 빈 Optional 반환
}

return readPcmData(convertedStream.get());
}

// 오디오 파일을 AudioInputStream으로 가져오기
private Optional<AudioInputStream> getAudioInputStream(File file) {
try {
return Optional.of(AudioSystem.getAudioInputStream(file));
} catch (UnsupportedAudioFileException | IOException e) {
log.error("오디오 스트림 생성 실패: {}", e.getMessage());
return Optional.empty(); // AudioInputStream 생성 실패 시 빈 Optional 반환
}
}

// 소스 포맷을 변환 가능 여부 확인 및 변환
private Optional<AudioInputStream> convertAudioStream(AudioInputStream sourceStream) {
AudioFormat sourceFormat = sourceStream.getFormat();

if (!AudioSystem.isConversionSupported(FORMAT, sourceFormat)) {
log.error("변환할 수 없는 오디오 포맷: {}", sourceFormat);
return Optional.empty(); // 변환 불가 시 빈 Optional 반환
}

return Optional.of(AudioSystem.getAudioInputStream(FORMAT, sourceStream));
}

// PCM 데이터를 읽고 반환
private Optional<byte[]> readPcmData(AudioInputStream convertedStream) {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = convertedStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, bytesRead);
}

byte[] rawPcmData = byteArrayOutputStream.toByteArray();
return Optional.of(formatWav2Raw(rawPcmData)); // PCM 데이터로 변환
} catch (IOException e) {
log.error("PCM 데이터 읽기 실패: {}", e.getMessage());
return Optional.empty(); // PCM 데이터 읽기 실패 시 빈 Optional 반환
}
}

// WAV 헤더를 제거하고 PCM 데이터만 반환
private byte[] formatWav2Raw(@NotNull final byte[] audioFileContent) {
return Arrays.copyOfRange(audioFileContent, HEADER_SIZE, audioFileContent.length);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Objects;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
Expand All @@ -22,10 +23,12 @@
import com.speech.up.api.etri.dto.ResponseRecognitionDto;
import com.speech.up.api.etri.type.ApiType;
import com.speech.up.api.etri.url.UrlCollector;
import com.speech.up.common.enums.StatusCode;
import com.speech.up.common.exception.custom.CustomIOException;
import com.speech.up.common.exception.http.BadRequestException;
import com.speech.up.report.service.ReportService;
import com.speech.up.record.entity.RecordEntity;
import com.speech.up.record.repository.RecordRepository;
import com.speech.up.report.service.ReportService;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -55,10 +58,13 @@ public void callRecognitionApi(String script, Long recordId) {
String audioContents = encodeAudioToBase64(recordEntity.getAudio());

RequestPronunciationDto pronunciationRequest = createPronunciationRequest(audioContents, script);
RequestRecognitionDto recognizedRequest = createRecognizedRequest(audioContents, recordEntity.getLanguageCode());
RequestRecognitionDto recognizedRequest = createRecognizedRequest(audioContents,
recordEntity.getLanguageCode());

ResponseEntity<ResponseRecognitionDto> recognizedResponse = sendPostRequest(ApiType.RECOGNITION, recognizedRequest, ResponseRecognitionDto.class);
ResponseEntity<ResponsePronunciationApiDto> pronunciationResponse = sendPostRequest(ApiType.PRONUNCIATION, pronunciationRequest, ResponsePronunciationApiDto.class);
ResponseEntity<ResponseRecognitionDto> recognizedResponse = sendPostRequest(ApiType.RECOGNITION,
recognizedRequest, ResponseRecognitionDto.class);
ResponseEntity<ResponsePronunciationApiDto> pronunciationResponse = sendPostRequest(ApiType.PRONUNCIATION,
pronunciationRequest, ResponsePronunciationApiDto.class);

handleApiResponses(recognizedResponse, pronunciationResponse);

Expand All @@ -67,7 +73,8 @@ public void callRecognitionApi(String script, Long recordId) {

saveReportIfValid(recognizedBody, pronunciationBody, recordEntity);
} catch (IOException e) {
throw new RuntimeException("Error during API request", e);
log.error(e.getMessage(), e);
throw new CustomIOException(StatusCode.IO_ERROR);
}
}

Expand All @@ -88,21 +95,17 @@ private RequestRecognitionDto createRecognizedRequest(String audioContents, Stri
return RequestRecognitionDto.createRecognition("reserved field", audioContents, languageCode);
}

private <T> ResponseEntity<T> sendPostRequest(ApiType apiType, Object requestDTO, Class<T> responseType) throws IOException {
private <T> ResponseEntity<T> sendPostRequest(ApiType apiType, Object requestDTO, Class<T> responseType) throws
IOException {
URL url = getUrl(apiType);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
HttpURLConnection con = (HttpURLConnection)url.openConnection();
configureConnection(con);

try (DataOutputStream wr = new DataOutputStream(con.getOutputStream())) {
String jsonRequest = gson.toJson(requestDTO);
log.info("Sending request to {} API: {}", apiType, jsonRequest);
wr.write(jsonRequest.getBytes(StandardCharsets.UTF_8));
wr.flush();
}
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
String jsonRequest = gson.toJson(requestDTO);
wr.write(jsonRequest.getBytes(StandardCharsets.UTF_8));
wr.flush();

int responseCode = con.getResponseCode();
log.info("Response code from {} API: {}", apiType, responseCode);

if (responseCode != HttpURLConnection.HTTP_OK) {
String errorResponse = readErrorResponse(con);
log.error("Error response from {} API: {}", apiType, errorResponse);
Expand Down Expand Up @@ -130,12 +133,11 @@ private void configureConnection(HttpURLConnection con) throws ProtocolException
}

private String readErrorResponse(HttpURLConnection con) throws IOException {
try (InputStream errorStream = con.getErrorStream()) {
if (errorStream != null) {
return new String(errorStream.readAllBytes(), StandardCharsets.UTF_8);
} else {
return "No error stream available";
}
InputStream errorStream = con.getErrorStream();
if (Objects.nonNull(errorStream)) {
return new String(errorStream.readAllBytes(), StandardCharsets.UTF_8);
} else {
return "No error stream available";
}
}

Expand All @@ -153,7 +155,7 @@ private void handleApiResponses(ResponseEntity<ResponseRecognitionDto> recognize
private void saveReportIfValid(ResponseRecognitionDto recognizedBody,
ResponsePronunciationApiDto pronunciationBody,
RecordEntity recordEntity) {
if (recognizedBody != null && pronunciationBody != null) {
if (Objects.nonNull(recognizedBody) && Objects.nonNull(pronunciationBody)) {
String recognized = recognizedBody.getReturn_object().getRecognized();
Double score = pronunciationBody.getReturn_object().getScore();
reportService.saveReport(recordEntity, recognized, score);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@

import com.speech.up.auth.filter.JwtAuthenticationFilter;
import com.speech.up.auth.handler.OAuth2SuccessHandler;
import com.speech.up.common.enums.StatusCode;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -69,7 +69,7 @@ protected SecurityFilterChain configure(HttpSecurity httpSecurity) throws Except
"/report", "/scripts", "/script-write", "/scripts-list", "/replies/**",
"/admin/view", "/page/me", "/speech-record", "reports/**", "/").permitAll()
.requestMatchers("/api/boards").hasAnyRole("ADMIN_USER", "GENERAL_USER")
.requestMatchers("/users/me").hasAnyRole("ADMIN_USER", "GENERAL_USER","BAN_USER")
.requestMatchers("/users/me").hasAnyRole("ADMIN_USER", "GENERAL_USER", "BAN_USER")
.requestMatchers("/speech-record").hasAnyRole("ADMIN_USER", "GENERAL_USER")
.requestMatchers("/speech-record/**").hasAnyRole("ADMIN_USER", "GENERAL_USER")
.requestMatchers("/speech-scripts/**").hasAnyRole("ADMIN_USER", "GENERAL_USER")
Expand Down Expand Up @@ -108,9 +108,9 @@ class FailedAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws
IOException, ServletException {
IOException {
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.getWriter().write("{\"code\" : \"NP\", \"message\" : \"No Permission\"}");
response.getWriter().write(String.valueOf(StatusCode.NO_AUTHORIZATION));
}
}
23 changes: 21 additions & 2 deletions src/main/java/com/speech/up/auth/entity/CustomOAuth2User.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package com.speech.up.auth.entity;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.user.OAuth2User;

import lombok.AllArgsConstructor;
Expand All @@ -14,19 +21,31 @@
public class CustomOAuth2User implements OAuth2User {

private String userId;
private String authorities; // 사용자 권한을 쉼표로 구분된 문자열로 저장

@Override
public Map<String, Object> getAttributes() {
return null;
Map<String, Object> attributes = new HashMap<>();
attributes.put("userId", userId);
return attributes;
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
return Objects.isNull(authorities) ? parseAuthorities(authorities) : Set.of();
}

@Override
public String getName() {
return this.userId;
}

// 권한 문자열을 파싱하여 GrantedAuthority의 컬렉션으로 변환
private Collection<? extends GrantedAuthority> parseAuthorities(String authorities) {
return Arrays.stream(authorities.split(","))
.map(String::trim)
.filter(auth -> !auth.isEmpty())
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
}
Loading