오픈소스 직접뷰어 기능 추가
This commit is contained in:
@@ -562,12 +562,29 @@ export async function renderDocViewer(resourcePath, docId) {
|
||||
await viewerMetadata(docVars.allDocData?.find((doc) => doc.doc_id === docId));
|
||||
}
|
||||
|
||||
// fallback-pdf-btn 숨김
|
||||
const docFallbackPdfBtn = document.getElementById('doc-fallback-pdf-btn');
|
||||
if (docFallbackPdfBtn) {
|
||||
docFallbackPdfBtn.style.display = 'none';
|
||||
}
|
||||
|
||||
let ext = splitBaseAndExt(resourcePath).ext.toLowerCase();
|
||||
|
||||
let excelDirectArr = ['xls', 'xlsx', 'xlsm'];
|
||||
let hwpDirectArr = ['hwp', 'hwpx'];
|
||||
let wordDirectArr = ['docx'];
|
||||
let isDirectView = excelDirectArr.includes(ext) || hwpDirectArr.includes(ext) || wordDirectArr.includes(ext);
|
||||
|
||||
let selectedDoc = docVars.allDocData?.find((doc) => doc.doc_id === docId);
|
||||
let previewKey = selectedDoc?.preview_key;
|
||||
let objectKey = selectedDoc?.object_key;
|
||||
|
||||
let targetKey = isDirectView ? objectKey : previewKey;
|
||||
|
||||
//Presigned URL
|
||||
let PresignedUrl = undefined;
|
||||
|
||||
let objectKey = docVars.allDocData?.find((doc) => doc.doc_id === docId)?.preview_key;
|
||||
if (objectKey == undefined || objectKey == `` || objectKey == null) {
|
||||
let ext = splitBaseAndExt(resourcePath).ext.toLowerCase();
|
||||
if (targetKey == undefined || targetKey == `` || targetKey == null) {
|
||||
let supportArr = ['hwp', 'hwpx', 'xls', 'xlsx', 'xlsm', 'ppt', 'pptx', 'doc', 'docx', 'dwg', 'dxf'];
|
||||
|
||||
if (!supportArr.includes(ext)) {
|
||||
@@ -580,7 +597,7 @@ export async function renderDocViewer(resourcePath, docId) {
|
||||
}
|
||||
|
||||
let generateDownloadUrlParams = {
|
||||
objectKey: objectKey,
|
||||
objectKey: targetKey,
|
||||
resourcePath: resourcePath,
|
||||
};
|
||||
let generateDownloadUrlRes = await axios.post(`${docVars.path_name}/generateDownloadDocUrl`, generateDownloadUrlParams);
|
||||
@@ -589,7 +606,6 @@ export async function renderDocViewer(resourcePath, docId) {
|
||||
}
|
||||
//Presigned URL end
|
||||
|
||||
let ext = splitBaseAndExt(resourcePath).ext.toLowerCase();
|
||||
let pdfArr = ['pdf', 'hwp', 'hwpx', 'xls', 'xlsx', 'xlsm', 'ppt', 'pptx', 'doc', 'docx', 'dwg', 'dxf'];
|
||||
let gsimArr = ['gsim'];
|
||||
let ifcArr = ['ifc'];
|
||||
@@ -601,7 +617,12 @@ export async function renderDocViewer(resourcePath, docId) {
|
||||
let threeArr = ['glb', 'gltf', 'obj', 'stl', 'fbx', '3dm'];
|
||||
let allArr = [...pdfArr, ...gsimArr, ...ifcArr, ...imageArr, ...videoArr, ...textArr, ...urlArr, ...zipArr, ...threeArr];
|
||||
if (allArr.includes(ext)) {
|
||||
if (pdfArr.includes(ext)) viewerPdf(PresignedUrl);
|
||||
let pdfArrFiltered = pdfArr.filter(e => !excelDirectArr.includes(e) && !hwpDirectArr.includes(e) && !wordDirectArr.includes(e));
|
||||
|
||||
if (pdfArrFiltered.includes(ext)) viewerPdf(PresignedUrl);
|
||||
if (excelDirectArr.includes(ext)) viewerExcel(PresignedUrl);
|
||||
if (hwpDirectArr.includes(ext)) viewerHwp(PresignedUrl);
|
||||
if (wordDirectArr.includes(ext)) viewerWord(PresignedUrl);
|
||||
if (gsimArr.includes(ext)) viewerGsim(PresignedUrl);
|
||||
if (ifcArr.includes(ext)) viewerIfc(PresignedUrl);
|
||||
if (threeArr.includes(ext)) viewer3d(PresignedUrl);
|
||||
@@ -642,6 +663,239 @@ export async function renderDocViewer(resourcePath, docId) {
|
||||
docVars.viewer.dataset.viewerType = 'convert';
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// 오픈소스 문서 직접 뷰잉 및 PDF 폴백 함수 정의 (Doc Viewer)
|
||||
// -----------------------------------------------------------------
|
||||
function initDocFallbackPdfButton(docId, resourcePath, objectKey, previewKey) {
|
||||
const btn = document.getElementById('doc-fallback-pdf-btn');
|
||||
if (!btn) return;
|
||||
|
||||
// 이전 등록된 리스너 제거를 위해 복사 대체
|
||||
const newBtn = btn.cloneNode(true);
|
||||
btn.parentNode.replaceChild(newBtn, btn);
|
||||
|
||||
newBtn.style.display = 'flex';
|
||||
newBtn.querySelector('.text').textContent = 'PDF로 보기';
|
||||
newBtn.style.pointerEvents = 'auto';
|
||||
|
||||
newBtn.addEventListener('click', async () => {
|
||||
newBtn.querySelector('.text').textContent = '로딩 중...';
|
||||
newBtn.style.pointerEvents = 'none';
|
||||
|
||||
try {
|
||||
// 1. 최신 메타데이터 (preview_key) 조회
|
||||
if (!previewKey) {
|
||||
await syncDocInfo(['official', 'attach', null]);
|
||||
let selectedDoc = docVars.allDocData?.find((doc) => doc.doc_id === docId);
|
||||
previewKey = selectedDoc?.preview_key;
|
||||
objectKey = selectedDoc?.object_key;
|
||||
}
|
||||
|
||||
// 2. 만약 PDF 변환본이 아직 없다면 백엔드 변환 요청
|
||||
if (!previewKey) {
|
||||
newBtn.querySelector('.text').textContent = 'PDF 변환 요청 중...';
|
||||
await convertDocPdf(resourcePath, docId);
|
||||
alert('서버 측 PDF 변환이 시작되었습니다. 잠시 후 다시 클릭해 주세요.');
|
||||
newBtn.querySelector('.text').textContent = 'PDF로 보기';
|
||||
newBtn.style.pointerEvents = 'auto';
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. PDF용 Presigned URL 생성
|
||||
let generateDownloadUrlParams = {
|
||||
objectKey: previewKey,
|
||||
resourcePath: resourcePath
|
||||
}
|
||||
let generateDownloadUrlRes = await axios.post(`${docVars.path_name}/generateDownloadDocUrl`, generateDownloadUrlParams);
|
||||
if (generateDownloadUrlRes.data.message == 'generateDownloadDocUrl_success') {
|
||||
let pdfUrl = generateDownloadUrlRes.data.url;
|
||||
|
||||
// 화면 초기화 및 PDF 뷰어 로드
|
||||
docVars.viewer = viewerWrap.querySelector('.viewer');
|
||||
docVars.viewer.innerHTML = '';
|
||||
newBtn.style.display = 'none';
|
||||
viewerPdf(pdfUrl);
|
||||
} else {
|
||||
alert('PDF 미리보기 주소 획득에 실패했습니다.');
|
||||
newBtn.querySelector('.text').textContent = 'PDF로 보기';
|
||||
newBtn.style.pointerEvents = 'auto';
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert('PDF 변환 및 조회 중 오류가 발생했습니다.');
|
||||
newBtn.querySelector('.text').textContent = 'PDF로 보기';
|
||||
newBtn.style.pointerEvents = 'auto';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function viewerExcel(presignedUrl) {
|
||||
docVars.viewer.innerHTML = '<div style="display:flex;justify-content:center;align-items:center;height:100%;font-size:1.2rem;color:#666;background:#fff;">엑셀 데이터를 불러오는 중...</div>';
|
||||
|
||||
fetch(presignedUrl)
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`);
|
||||
return res.arrayBuffer();
|
||||
})
|
||||
.then(arrayBuffer => {
|
||||
docVars.viewer.innerHTML = '';
|
||||
|
||||
LuckyExcel.transformExcelToLucky(arrayBuffer, function(exportJson, luckysheetfile) {
|
||||
if(exportJson.sheets == null || exportJson.sheets.length == 0) {
|
||||
docVars.viewer.innerHTML = '<div style="display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%;color:#d9534f;font-size:1.1rem;background:#fff;">엑셀 데이터를 파싱하지 못했습니다. (xls 확장자는 지원하지 않습니다.)</div>';
|
||||
initDocFallbackPdfButton(docId, resourcePath, objectKey, previewKey);
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.luckysheet) {
|
||||
window.luckysheet.destroy();
|
||||
}
|
||||
|
||||
docVars.viewer.style.position = 'relative';
|
||||
const container = document.createElement('div');
|
||||
container.id = 'luckysheet_inner_doc';
|
||||
container.style.margin = '0px';
|
||||
container.style.padding = '0px';
|
||||
container.style.position = 'absolute';
|
||||
container.style.width = '100%';
|
||||
container.style.height = '100%';
|
||||
container.style.left = '0px';
|
||||
container.style.top = '0px';
|
||||
docVars.viewer.appendChild(container);
|
||||
|
||||
try {
|
||||
window.luckysheet.create({
|
||||
container: 'luckysheet_inner_doc',
|
||||
data: exportJson.sheets,
|
||||
title: exportJson.info.name || 'Excel Viewer',
|
||||
lang: 'en',
|
||||
showinfobar: false,
|
||||
myFolderUrl: 'javascript:void(0)'
|
||||
});
|
||||
} catch (createErr) {
|
||||
console.error("Luckysheet create error: ", createErr);
|
||||
docVars.viewer.innerHTML = `<div style="display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%;color:#d9534f;background:#fff;">
|
||||
<div>엑셀 시트 생성 중 오류가 발생했습니다.</div>
|
||||
<div style="font-size:0.9rem;color:#999;margin-top:8px;">에러: ${createErr.message}</div>
|
||||
</div>`;
|
||||
}
|
||||
}, function(err) {
|
||||
console.error("Luckysheet transform error: ", err);
|
||||
docVars.viewer.innerHTML = `<div style="display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%;color:#d9534f;background:#fff;">
|
||||
<div>엑셀 파일을 읽는 중 오류가 발생했습니다.</div>
|
||||
<div style="font-size:0.9rem;color:#999;margin-top:8px;">상세: ${err.message || err}</div>
|
||||
</div>`;
|
||||
initDocFallbackPdfButton(docId, resourcePath, objectKey, previewKey);
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
docVars.viewer.innerHTML = `<div style="display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%;color:#d9534f;background:#fff;">
|
||||
<div>엑셀 파일을 불러오는데 실패했습니다.</div>
|
||||
<div style="font-size:0.9rem;color:#999;margin-top:8px;">에러: ${err.message}</div>
|
||||
</div>`;
|
||||
initDocFallbackPdfButton(docId, resourcePath, objectKey, previewKey);
|
||||
});
|
||||
|
||||
docVars.viewer.dataset.viewerType = 'excel';
|
||||
}
|
||||
|
||||
function viewerWord(presignedUrl) {
|
||||
docVars.viewer.innerHTML = '<div style="display:flex;justify-content:center;align-items:center;height:100%;font-size:1.2rem;color:#666;background:#fff;">워드 문서를 불러오는 중...</div>';
|
||||
initDocFallbackPdfButton(docId, resourcePath, objectKey, previewKey);
|
||||
|
||||
fetch(presignedUrl)
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error('Word fetch failed');
|
||||
return res.arrayBuffer();
|
||||
})
|
||||
.then(arrayBuffer => {
|
||||
docVars.viewer.innerHTML = '';
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.style.width = '100%';
|
||||
container.style.height = '100%';
|
||||
container.style.overflow = 'auto';
|
||||
container.style.padding = '20px';
|
||||
container.style.boxSizing = 'border-box';
|
||||
container.style.background = '#f5f5f5';
|
||||
|
||||
const docxInner = document.createElement('div');
|
||||
docxInner.style.background = '#ffffff';
|
||||
docxInner.style.margin = '0 auto';
|
||||
docxInner.style.maxWidth = '800px';
|
||||
docxInner.style.boxShadow = '0 4px 10px rgba(0,0,0,0.1)';
|
||||
docxInner.style.padding = '40px';
|
||||
|
||||
container.appendChild(docxInner);
|
||||
docVars.viewer.appendChild(container);
|
||||
|
||||
docx.renderAsync(arrayBuffer, docxInner)
|
||||
.then(() => console.log("docx rendered"))
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
docxInner.innerHTML = '<div style="color:#d9534f;text-align:center;">워드 문서 파싱 중 오류가 발생했습니다. 상단의 "PDF로 보기" 버튼을 이용해 주세요.</div>';
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
docVars.viewer.innerHTML = '<div style="display:flex;justify-content:center;align-items:center;height:100%;color:#d9534f;background:#fff;">워드 문서를 불러오는데 실패했습니다.</div>';
|
||||
});
|
||||
|
||||
docVars.viewer.dataset.viewerType = 'word';
|
||||
}
|
||||
|
||||
function viewerHwp(presignedUrl) {
|
||||
docVars.viewer.innerHTML = '<div style="display:flex;justify-content:center;align-items:center;height:100%;font-size:1.2rem;color:#666;background:#fff;">한글 문서를 불러오는 중...</div>';
|
||||
initDocFallbackPdfButton(docId, resourcePath, objectKey, previewKey);
|
||||
|
||||
fetch(presignedUrl)
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error('HWP fetch failed');
|
||||
return res.blob();
|
||||
})
|
||||
.then(blob => {
|
||||
docVars.viewer.innerHTML = '';
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.style.width = '100%';
|
||||
container.style.height = '100%';
|
||||
container.style.overflow = 'auto';
|
||||
container.style.padding = '20px';
|
||||
container.style.boxSizing = 'border-box';
|
||||
container.style.background = '#f5f5f5';
|
||||
|
||||
const hwpInner = document.createElement('div');
|
||||
hwpInner.style.background = '#ffffff';
|
||||
hwpInner.style.margin = '0 auto';
|
||||
hwpInner.style.maxWidth = '800px';
|
||||
hwpInner.style.boxShadow = '0 4px 10px rgba(0,0,0,0.1)';
|
||||
hwpInner.style.padding = '40px';
|
||||
hwpInner.style.minHeight = '100%';
|
||||
|
||||
container.appendChild(hwpInner);
|
||||
docVars.viewer.appendChild(container);
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const bstr = e.target.result;
|
||||
try {
|
||||
new hwp.Viewer(hwpInner, bstr);
|
||||
} catch (err) {
|
||||
console.error("hwp.js error: ", err);
|
||||
hwpInner.innerHTML = '<div style="color:#d9534f;text-align:center;">한글 문서 파싱 중 오류가 발생했습니다. 상단의 "PDF로 보기" 버튼을 이용해 주세요.</div>';
|
||||
}
|
||||
};
|
||||
reader.readAsBinaryString(blob);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
docVars.viewer.innerHTML = '<div style="display:flex;justify-content:center;align-items:center;height:100%;color:#d9534f;background:#fff;">한글 문서를 불러오는데 실패했습니다.</div>';
|
||||
});
|
||||
|
||||
docVars.viewer.dataset.viewerType = 'hwp';
|
||||
}
|
||||
|
||||
async function viewerPdf(PresignedUrl) {
|
||||
resetViewer();
|
||||
|
||||
@@ -945,6 +1199,16 @@ function resetViewer() {
|
||||
}
|
||||
}
|
||||
|
||||
if (docVars.viewer.dataset.viewerType == 'excel') {
|
||||
if (window.luckysheet) {
|
||||
try {
|
||||
window.luckysheet.destroy();
|
||||
} catch (e) {
|
||||
console.error("Luckysheet destroy error: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
docVars.viewer.dataset.viewerType = '';
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user