You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
621 lines
15 KiB
Java
621 lines
15 KiB
Java
package cokr.xit.base.file;
|
|
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileOutputStream;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.net.URLConnection;
|
|
import java.util.ArrayList;
|
|
import java.util.Base64;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Stream;
|
|
|
|
import cokr.xit.foundation.AbstractComponent;
|
|
import cokr.xit.foundation.AbstractEntity;
|
|
|
|
/**파일 정보
|
|
* @author mjkhan
|
|
*/
|
|
public class FileInfo extends AbstractEntity {
|
|
/**파일의 관계 정보
|
|
* @author mjkhan
|
|
*/
|
|
public static class Relation {
|
|
private String infoType;
|
|
private String infoKey;
|
|
private String subType;
|
|
|
|
/**objs를 대시(-)로 연결하여 infoKey를 반환한다.
|
|
* @param objs 아이디로 쓰이는 필드값
|
|
* @return infoKey
|
|
*/
|
|
public static String InfoKey(Object... objs) {
|
|
if (isEmpty(objs))
|
|
return "";
|
|
|
|
return Stream.of(objs)
|
|
.filter(obj -> !isEmpty(obj))
|
|
.map(obj -> obj.toString())
|
|
.collect(Collectors.joining("-"));
|
|
}
|
|
|
|
/**관계 정보의 유형을 반환한다.
|
|
* @return 관계 정보의 유형
|
|
*/
|
|
public String getInfoType() {
|
|
return infoType;
|
|
}
|
|
|
|
/**관계 정보의 유형을 설정한다.
|
|
* @param infoType 관계 정보의 유형
|
|
* @return 현재 Relation
|
|
*/
|
|
public Relation setInfoType(String infoType) {
|
|
this.infoType = infoType;
|
|
return this;
|
|
}
|
|
|
|
/**관계 정보의 키를 반환한다.
|
|
* @return 관계 정보의 키
|
|
*/
|
|
public String getInfoKey() {
|
|
return infoKey;
|
|
}
|
|
|
|
/**관계 정보의 키를 설정한다.
|
|
* @param infoKey 관계 정보의 키를
|
|
* @return 현재 Relation
|
|
*/
|
|
public Relation setInfoKey(String infoKey) {
|
|
this.infoKey = infoKey;
|
|
return this;
|
|
}
|
|
|
|
/**관계 정보의 하위 유형을 반환한다.
|
|
* @return 관계 정보의 하위 유형
|
|
*/
|
|
public String getSubType() {
|
|
return subType;
|
|
}
|
|
|
|
/**관계 정보의 하위 유형을 설정한다.
|
|
* @param subType 관계 정보의 하위 유형
|
|
* @return 현재 Relation
|
|
*/
|
|
public Relation setSubType(String subType) {
|
|
this.subType = subType;
|
|
return this;
|
|
}
|
|
|
|
/**관계 정보의 유형, 키, 하위 유형을 fileInfo에 설정한다.
|
|
* @param fileInfo FileInfo
|
|
*/
|
|
public void setInfo(FileInfo fileInfo) {
|
|
fileInfo.setInfoType(notEmpty(infoType, "infoType"));
|
|
fileInfo.setInfoKey(notEmpty(infoKey, "infoKey"));
|
|
if (!isEmpty(subType))
|
|
fileInfo.setSubType(subType);
|
|
}
|
|
}
|
|
|
|
/**파일 데이터 홀더
|
|
* @author mjkhan
|
|
*/
|
|
public static class DataHolder {
|
|
private Relation relation;
|
|
private String filename;
|
|
private byte[] data;
|
|
|
|
/**새 DataHolder를 생성한다.
|
|
* @param relation 관계 정보
|
|
* @param filename 파일 이름
|
|
* @param data 파일 데이터
|
|
*/
|
|
public DataHolder(Relation relation, String filename, byte[] data) {
|
|
this.relation = relation;
|
|
this.filename = filename;
|
|
this.data = data != null ? data : new byte[0];
|
|
}
|
|
|
|
/**새 DataHolder를 생성한다.
|
|
* @param relation 관계 정보
|
|
* @param filename 파일 이름
|
|
* @param data 파일 데이터
|
|
*/
|
|
public DataHolder(Relation relation, String filename, String data) {
|
|
this(relation, filename, !FileInfo.isEmpty(data) ? Base64.getDecoder().decode(data.getBytes()) : null);
|
|
}
|
|
|
|
/**관계 정보를 반환한다.
|
|
* @return 관계 정보
|
|
*/
|
|
public Relation getRelation() {
|
|
return relation;
|
|
}
|
|
|
|
/**파일이름을 반환한다.
|
|
* @return 파일이름
|
|
*/
|
|
public String getFilename() {
|
|
return filename;
|
|
}
|
|
|
|
/**데이터를 반환한다.
|
|
* @return 데이터
|
|
*/
|
|
public byte[] getData() {
|
|
return data;
|
|
}
|
|
|
|
/**데이터의 길이를 반환한다.
|
|
* @return 데이터의 길이
|
|
*/
|
|
public long length() {
|
|
return data != null ? data.length : 0;
|
|
}
|
|
|
|
/**데이터가 비어있는지 반환한다.
|
|
* @return
|
|
* <ul><li>데이터가 비어있으면 true</li>
|
|
* <li>그렇지 않으면 false</li>
|
|
* </ul>
|
|
*/
|
|
public boolean isEmpty() {
|
|
return length() < 1;
|
|
}
|
|
}
|
|
|
|
/**FileInfo 생성자
|
|
* @author mjkhan
|
|
*/
|
|
public static class Factory extends AbstractComponent {
|
|
/**주어진 관계 정보 유형과 키, 그리고 파일로 FileInfo 목록을 생성한다.
|
|
* @param relation 관계 정보
|
|
* @param files 파일
|
|
* @return FileInfo 목록
|
|
*/
|
|
public List<FileInfo> createFileInfos(Relation relation, Iterable<File> files) {
|
|
if (isEmpty(files))
|
|
return Collections.emptyList();
|
|
|
|
ArrayList<FileInfo> result = new ArrayList<>();
|
|
for (File file: files)
|
|
try {
|
|
if (file == null) continue;
|
|
|
|
FileInfo info = new FileInfo();
|
|
if (relation != null)
|
|
relation.setInfo(info);
|
|
String filename = file.getName();
|
|
info.setName(filename);
|
|
InputStream input = new FileInputStream(file);
|
|
String mimeType = URLConnection.guessContentTypeFromName(filename);
|
|
if (isEmpty(mimeType))
|
|
mimeType = URLConnection.guessContentTypeFromStream(input);
|
|
info.setMimeType(mimeType);
|
|
info.setInputStream(input);
|
|
info.setSize(file.length());
|
|
info.setSortOrder(result.size());
|
|
result.add(info);
|
|
} catch (Exception e) {
|
|
throw runtimeException(e);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**주어진 관계 정보 유형과 키, 그리고 파일로 FileInfo 목록을 생성한다.
|
|
* @param relation 관계 정보
|
|
* @param files 파일
|
|
* @return FileInfo 목록
|
|
*/
|
|
public List<FileInfo> createFileInfos(Relation relation, File... files) {
|
|
return createFileInfos(relation, List.of(files));
|
|
}
|
|
|
|
/**주어진 관계 정보 유형과 키, 그리고 파일로 FileInfo을 생성한다.
|
|
* @param relation 관계 정보
|
|
* @param file 파일
|
|
* @return FileInfo
|
|
*/
|
|
public FileInfo create(Relation relation, File file) {
|
|
List<FileInfo> fileInfos = createFileInfos(relation, file);
|
|
return !fileInfos.isEmpty() ? fileInfos.get(0) : null;
|
|
}
|
|
|
|
/**파일 DataHolder에서 FileInfo 목록을 생성한다.
|
|
* @return FileInfo 목록
|
|
*/
|
|
public List<FileInfo> createFileInfos(Iterable<DataHolder> dataHolders) {
|
|
if (isEmpty(dataHolders))
|
|
return Collections.emptyList();
|
|
|
|
ArrayList<FileInfo> result = new ArrayList<>();
|
|
for (DataHolder holder: dataHolders)
|
|
try {
|
|
if (holder == null) continue;
|
|
|
|
FileInfo info = new FileInfo();
|
|
Relation relation = holder.getRelation();
|
|
if (relation != null)
|
|
relation.setInfo(info);
|
|
String filename = holder.getFilename();
|
|
info.setName(filename);
|
|
InputStream input = new ByteArrayInputStream(holder.data);
|
|
String mimeType = URLConnection.guessContentTypeFromName(filename);
|
|
if (isEmpty(mimeType))
|
|
mimeType = URLConnection.guessContentTypeFromStream(input);
|
|
info.setMimeType(mimeType);
|
|
info.setInputStream(input);
|
|
info.setSize(holder.length());
|
|
info.setSortOrder(result.size());
|
|
result.add(info);
|
|
} catch (Exception e) {
|
|
throw runtimeException(e);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**파일 DataHolder에서 FileInfo 목록을 생성한다.
|
|
* @return FileInfo 목록
|
|
*/
|
|
public List<FileInfo> createFileInfos(DataHolder... dataHolders) {
|
|
return createFileInfos(List.of(dataHolders));
|
|
}
|
|
|
|
/**파일 DataHolder에서 FileInfo를 생성한다.
|
|
* @return FileInfo
|
|
*/
|
|
public FileInfo create(DataHolder dataHolder) {
|
|
List<FileInfo> list = createFileInfos(dataHolder);
|
|
return !list.isEmpty() ? list.get(0) : null;
|
|
}
|
|
}
|
|
|
|
private String
|
|
id,
|
|
infoType,
|
|
infoKey,
|
|
subType,
|
|
subCode,
|
|
name,
|
|
path,
|
|
url,
|
|
mimeType,
|
|
extension,
|
|
etcInfo;
|
|
private long size;
|
|
private int downloadCount;
|
|
private int sortOrder;
|
|
private InputStream input;
|
|
|
|
/**주어진 경로에서 파일 이름을 추출한다.
|
|
* @param path 파일 경로
|
|
* @return 파일 이름
|
|
*/
|
|
public static final String name(String path) {
|
|
path = path.replace("\\", "/");
|
|
return path.substring(path.lastIndexOf("/") + 1);
|
|
}
|
|
|
|
/**주어진 경로에서 디렉토리 이름을 추출한다.
|
|
* @param path 파일 경로
|
|
* @return 디렉토리 이름
|
|
*/
|
|
public static final String dir(String path) {
|
|
path = path.replace("\\", "/");
|
|
int pos = path.lastIndexOf("/");
|
|
return pos < 0 ? "" : path.substring(0, pos);
|
|
}
|
|
|
|
/**주어진 파일 이름에서 확장자를 추출한다.
|
|
* @param filename 파일 이름
|
|
* @return 파일 확장자
|
|
*/
|
|
public static final String extension(String filename) {
|
|
int pos = filename.lastIndexOf(".");
|
|
return pos < 0 ? "" : filename.substring(pos + 1);
|
|
}
|
|
|
|
/**id를 반환한다.
|
|
* @return id
|
|
*/
|
|
public String getId() {
|
|
return id;
|
|
}
|
|
|
|
/**id를 설정한다.
|
|
* @param id id
|
|
*/
|
|
public void setId(String id) {
|
|
this.id = id;
|
|
}
|
|
|
|
/**관계 정보 유형을 반환한다. 프로그램에서 정의
|
|
* @return 관계 정보 유형
|
|
*/
|
|
public String getInfoType() {
|
|
return infoType;
|
|
}
|
|
|
|
/**관계 정보 유형을 설정한다. 프로그램에서 정의
|
|
* @param infoType 관계 정보 유형
|
|
* @return 현재 FileInfo
|
|
*/
|
|
public FileInfo setInfoType(String infoType) {
|
|
this.infoType = infoType;
|
|
return this;
|
|
}
|
|
|
|
/**관계 정보 키를 반환한다. 프로그램에서 정의
|
|
* @return 관계 정보 키
|
|
*/
|
|
public String getInfoKey() {
|
|
return infoKey;
|
|
}
|
|
|
|
/**관계 정보 키를 설정한다. 프로그램에서 정의
|
|
* @param infoKey 관계 정보 키
|
|
* @return 현재 FileInfo
|
|
*/
|
|
public FileInfo setInfoKey(String infoKey) {
|
|
this.infoKey = infoKey;
|
|
return this;
|
|
}
|
|
|
|
/**관계 정보 기준 하위분류를 반환한다. 프로그램에서 정의
|
|
* @return 관계 정보 기준 하위분류
|
|
*/
|
|
public String getSubType() {
|
|
return subType;
|
|
}
|
|
|
|
/**관계 정보 기준 하위분류를 설정한다. 프로그램에서 정의
|
|
* @param subType 관계 정보 기준 하위분류
|
|
* @return 현재 FileInfo
|
|
*/
|
|
public FileInfo setSubType(String subType) {
|
|
this.subType = subType;
|
|
return this;
|
|
}
|
|
|
|
/**관계 정보 기준 하위코드를 반환한다. 프로그램에서 정의.
|
|
* @return 관계 정보 기준 하위코드
|
|
*/
|
|
public String getSubCode() {
|
|
return subCode;
|
|
}
|
|
|
|
/**관계 정보 기준 하위코드를 설정한다. 프로그램에서 정의.
|
|
* @param subCode 관계 정보 기준 하위코드
|
|
* @return 현재 FileInfo
|
|
*/
|
|
public FileInfo setSubCode(String subCode) {
|
|
this.subCode = subCode;
|
|
return this;
|
|
}
|
|
|
|
/**파일이름을 반환한다.
|
|
* @return 파일이름
|
|
*/
|
|
public String getName() {
|
|
return name;
|
|
}
|
|
|
|
/**파일이름을 설정한다.
|
|
* @param name 파일이름
|
|
*/
|
|
public void setName(String name) {
|
|
this.name = name;
|
|
}
|
|
|
|
/**저장경로를 반환한다.
|
|
* @return 저장경로
|
|
*/
|
|
public String getPath() {
|
|
return path;
|
|
}
|
|
|
|
/**저장경로를 설정한다.
|
|
* @param path 저장경로
|
|
* @return 현재 FileInfo
|
|
*/
|
|
public FileInfo setPath(String path) {
|
|
this.path = path;
|
|
return this;
|
|
}
|
|
|
|
/**url을 반환한다.
|
|
* @return url
|
|
*/
|
|
public String getUrl() {
|
|
return url;
|
|
}
|
|
|
|
/**url을 설정한다.
|
|
* @param url url
|
|
*/
|
|
public void setUrl(String url) {
|
|
this.url = url;
|
|
}
|
|
|
|
/**mime type을 반환한다.
|
|
* @return mime type
|
|
*/
|
|
public String getMimeType() {
|
|
return ifEmpty(mimeType, () -> mimeType = "application/octet-stream");
|
|
}
|
|
|
|
/**mime type을 설정한다.
|
|
* @param mimeType mime type
|
|
*/
|
|
public void setMimeType(String mimeType) {
|
|
this.mimeType = mimeType;
|
|
}
|
|
|
|
/**확장자를 반환한다.
|
|
* @return 확장자
|
|
*/
|
|
public String getExtension() {
|
|
return ifEmpty(extension, () -> extension = extension(name));
|
|
}
|
|
|
|
/**확장자를 설정한다.
|
|
* @param extension 확장자
|
|
*/
|
|
public void setExtension(String extension) {
|
|
this.extension = extension;
|
|
}
|
|
|
|
/**기타 추가정보를 반환한다. 프로그램에서 정의.
|
|
* @return 추가정보
|
|
*/
|
|
public String getEtcInfo() {
|
|
return etcInfo;
|
|
}
|
|
|
|
/**기타 추가정보를 설정한다. 프로그램에서 정의.
|
|
* @param etcInfo 추가정보
|
|
*/
|
|
public void setEtcInfo(String etcInfo) {
|
|
this.etcInfo = etcInfo;
|
|
}
|
|
|
|
/**파일크기를 반환한다.
|
|
* @return 파일크기
|
|
*/
|
|
public long getSize() {
|
|
return size;
|
|
}
|
|
|
|
/**파일크기를 설정한다.
|
|
* @param size 파일크기
|
|
*/
|
|
public void setSize(long size) {
|
|
this.size = size;
|
|
}
|
|
|
|
/**다운로드 횟수를 반환한다.
|
|
* @return 다운로드 횟수
|
|
*/
|
|
public int getDownloadCount() {
|
|
return downloadCount;
|
|
}
|
|
|
|
/**다운로드 횟수를 설정한다.
|
|
* @param downloadCount 다운로드 횟수
|
|
*/
|
|
public void setDownloadCount(int downloadCount) {
|
|
if (downloadCount < 0)
|
|
throw new IllegalArgumentException("downloadCount < 0");
|
|
this.downloadCount = downloadCount;
|
|
}
|
|
|
|
/**정렬순서를 반환한다.
|
|
* @return 정렬순서
|
|
*/
|
|
public int getSortOrder() {
|
|
return sortOrder;
|
|
}
|
|
|
|
/**정렬순서를 설정한다.
|
|
* @param sortOrder 정렬순서
|
|
*/
|
|
public void setSortOrder(int sortOrder) {
|
|
this.sortOrder = sortOrder;
|
|
}
|
|
|
|
/**FileInfo 경로의 InputStream을 반환한다.
|
|
* @return InputStream
|
|
*/
|
|
public InputStream getInputStream() {
|
|
if (input != null) return input;
|
|
|
|
if (isEmpty(path)) return null;
|
|
|
|
try {
|
|
return new FileInputStream(path);
|
|
} catch (Exception e) {
|
|
throw runtimeException(e);
|
|
}
|
|
}
|
|
|
|
/**InputStream을 설정한다.
|
|
* @param input InputStream
|
|
* @return FileInfo
|
|
*/
|
|
public FileInfo setInputStream(InputStream input) {
|
|
this.input = input;
|
|
return this;
|
|
}
|
|
|
|
/**OutputStream으로 File의 데이터를 저장한다.
|
|
* @param out OutputStream
|
|
* @return 저장 여부
|
|
* <ul><li>저장됐으면 true</li>
|
|
* <li>그렇지 않으면 false</li>
|
|
* </ul>
|
|
*/
|
|
public boolean write(OutputStream out) {
|
|
if (out == null) return false;
|
|
|
|
try (
|
|
InputStream in = getInputStream();
|
|
) {
|
|
in.transferTo(out);
|
|
return true;
|
|
} catch (Exception e) {
|
|
throw runtimeException(e);
|
|
}
|
|
}
|
|
|
|
/**FileInfo의 경로로 File을 저장한다.
|
|
* @return 저장 여부
|
|
* <ul><li>저장됐으면 true</li>
|
|
* <li>그렇지 않으면 false</li>
|
|
* </ul>
|
|
*/
|
|
public boolean write() {
|
|
try (FileOutputStream out = new FileOutputStream(path)) {
|
|
return write(out);
|
|
} catch(Exception e) {
|
|
throw runtimeException(e);
|
|
}
|
|
}
|
|
|
|
/**FileInfo의 File을 삭제한다.
|
|
* @return 삭제 여부
|
|
* <ul><li>삭제됐으면 true</li>
|
|
* <li>그렇지 않으면 false</li>
|
|
* </ul>
|
|
*/
|
|
public boolean delete() {
|
|
if (isEmpty(path)) return false;
|
|
|
|
File file = new File(path);
|
|
return file.exists() ? file.delete() : false;
|
|
}
|
|
|
|
/**InputStream을 비운다.
|
|
* @return FileInfo
|
|
*/
|
|
public FileInfo close() {
|
|
try {
|
|
if (input != null)
|
|
input.close();
|
|
input = null;
|
|
return this;
|
|
} catch (Exception e) {
|
|
throw runtimeException(e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
String path = getPath();
|
|
return getName() + (isEmpty(path) ? "" : " -> " + path);
|
|
}
|
|
} |