@web-portal/core-infrastructure 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/web-portal-core-infrastructure.mjs +435 -15
- package/fesm2022/web-portal-core-infrastructure.mjs.map +1 -1
- package/lib/services/file-aia-gcs.service.d.ts +16 -1
- package/lib/services/file-gcs.service.d.ts +16 -1
- package/lib/services/file-wap-gcs.service.d.ts +3 -8
- package/lib/services/file.service.d.ts +6 -0
- package/package.json +1 -1
|
@@ -1687,6 +1687,9 @@ class FileGCSService {
|
|
|
1687
1687
|
getSignedLink(filePath) {
|
|
1688
1688
|
return this._apiService.post(`${this.baseUrl}/getSignedLink?filePath=${filePath}`);
|
|
1689
1689
|
}
|
|
1690
|
+
getPresignedLink(fileName) {
|
|
1691
|
+
return this._apiService.post(`${this.baseUrl}/getPresignedLink?fileName=${fileName}`);
|
|
1692
|
+
}
|
|
1690
1693
|
attachFile(file) {
|
|
1691
1694
|
const formData = new FormData();
|
|
1692
1695
|
formData.append("file", file);
|
|
@@ -1705,6 +1708,206 @@ class FileGCSService {
|
|
|
1705
1708
|
});
|
|
1706
1709
|
return firstValueFrom(this._http.request(req));
|
|
1707
1710
|
}
|
|
1711
|
+
/**
|
|
1712
|
+
* Build an Error object but keep HTTP-related metadata for callers that need to branch by status.
|
|
1713
|
+
* Note: We intentionally do NOT change business flow; we only enrich the error instance.
|
|
1714
|
+
*/
|
|
1715
|
+
buildHttpError(message, meta) {
|
|
1716
|
+
const err = new Error(message);
|
|
1717
|
+
if (meta?.status !== undefined)
|
|
1718
|
+
err.status = meta.status;
|
|
1719
|
+
if (meta?.statusText !== undefined)
|
|
1720
|
+
err.statusText = meta.statusText;
|
|
1721
|
+
if (meta?.url !== undefined)
|
|
1722
|
+
err.url = meta.url;
|
|
1723
|
+
if (meta?.cause !== undefined)
|
|
1724
|
+
err.cause = meta.cause;
|
|
1725
|
+
return err;
|
|
1726
|
+
}
|
|
1727
|
+
uploadFile(file) {
|
|
1728
|
+
return new Observable((observer) => {
|
|
1729
|
+
let xhr = null;
|
|
1730
|
+
// Tạo tên file unique để tránh trùng tên khi upload nhiều lần
|
|
1731
|
+
const uniqueFileName = this.generateUniqueFileName(file.name);
|
|
1732
|
+
firstValueFrom(this.getPresignedLink(uniqueFileName))
|
|
1733
|
+
.then((presignedData) => {
|
|
1734
|
+
if (!presignedData) {
|
|
1735
|
+
throw new Error("Không nhận được presigned link từ server");
|
|
1736
|
+
}
|
|
1737
|
+
const presignedURL = presignedData.PreSignedURL ||
|
|
1738
|
+
presignedData.preSignedURL ||
|
|
1739
|
+
presignedData.url;
|
|
1740
|
+
const filePath = presignedData.FilePath || presignedData.filePath;
|
|
1741
|
+
const absoluteURL = presignedData.AbsoluteURL || presignedData.absoluteURL;
|
|
1742
|
+
if (!presignedURL) {
|
|
1743
|
+
throw new Error("Presigned URL không hợp lệ");
|
|
1744
|
+
}
|
|
1745
|
+
const contentType = presignedData.ContentType ||
|
|
1746
|
+
presignedData.contentType ||
|
|
1747
|
+
file.type ||
|
|
1748
|
+
"application/octet-stream";
|
|
1749
|
+
xhr = new XMLHttpRequest();
|
|
1750
|
+
const cleanup = () => {
|
|
1751
|
+
if (xhr) {
|
|
1752
|
+
xhr.upload.removeEventListener("progress", progressHandler);
|
|
1753
|
+
xhr.removeEventListener("load", loadHandler);
|
|
1754
|
+
xhr.removeEventListener("error", errorHandler);
|
|
1755
|
+
xhr.removeEventListener("timeout", timeoutHandler);
|
|
1756
|
+
xhr.abort();
|
|
1757
|
+
xhr = null;
|
|
1758
|
+
}
|
|
1759
|
+
};
|
|
1760
|
+
const progressHandler = (event) => {
|
|
1761
|
+
if (event.lengthComputable) {
|
|
1762
|
+
observer.next({
|
|
1763
|
+
type: HttpEventType.UploadProgress,
|
|
1764
|
+
loaded: event.loaded,
|
|
1765
|
+
total: event.total,
|
|
1766
|
+
});
|
|
1767
|
+
}
|
|
1768
|
+
};
|
|
1769
|
+
const loadHandler = async () => {
|
|
1770
|
+
if (xhr && xhr.status === 200) {
|
|
1771
|
+
try {
|
|
1772
|
+
const finalUrl = absoluteURL || presignedURL;
|
|
1773
|
+
const result = this.buildResult(file, filePath, finalUrl);
|
|
1774
|
+
cleanup();
|
|
1775
|
+
observer.next(new HttpResponse({
|
|
1776
|
+
body: result,
|
|
1777
|
+
status: 200,
|
|
1778
|
+
statusText: "OK",
|
|
1779
|
+
url: presignedURL,
|
|
1780
|
+
}));
|
|
1781
|
+
observer.complete();
|
|
1782
|
+
}
|
|
1783
|
+
catch (err) {
|
|
1784
|
+
cleanup();
|
|
1785
|
+
const msg = err?.message ||
|
|
1786
|
+
"Upload thất bại. Vui lòng thử lại.";
|
|
1787
|
+
observer.error(this.buildHttpError(msg, {
|
|
1788
|
+
status: xhr?.status,
|
|
1789
|
+
statusText: xhr?.statusText,
|
|
1790
|
+
url: presignedURL,
|
|
1791
|
+
cause: err,
|
|
1792
|
+
}));
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
else if (xhr) {
|
|
1796
|
+
const errorMessage = `Upload file thất bại: ${xhr.statusText} (${xhr.status})`;
|
|
1797
|
+
cleanup();
|
|
1798
|
+
observer.error(this.buildHttpError(errorMessage, {
|
|
1799
|
+
status: xhr.status,
|
|
1800
|
+
statusText: xhr.statusText,
|
|
1801
|
+
url: presignedURL,
|
|
1802
|
+
}));
|
|
1803
|
+
}
|
|
1804
|
+
};
|
|
1805
|
+
const errorHandler = () => {
|
|
1806
|
+
if (!xhr)
|
|
1807
|
+
return;
|
|
1808
|
+
let userMessage = "Upload file thất bại";
|
|
1809
|
+
if (xhr.status === 0) {
|
|
1810
|
+
userMessage =
|
|
1811
|
+
"Không thể kết nối đến máy chủ lưu trữ. Máy chủ không phản hồi.";
|
|
1812
|
+
}
|
|
1813
|
+
else if (xhr.status >= 500) {
|
|
1814
|
+
userMessage = "Lỗi máy chủ. Vui lòng thử lại sau.";
|
|
1815
|
+
}
|
|
1816
|
+
else if (xhr.status >= 400) {
|
|
1817
|
+
userMessage = `Lỗi yêu cầu: ${xhr.statusText}`;
|
|
1818
|
+
}
|
|
1819
|
+
cleanup();
|
|
1820
|
+
observer.error(this.buildHttpError(userMessage, {
|
|
1821
|
+
status: xhr.status,
|
|
1822
|
+
statusText: xhr.statusText,
|
|
1823
|
+
url: presignedURL,
|
|
1824
|
+
}));
|
|
1825
|
+
};
|
|
1826
|
+
const timeoutHandler = () => {
|
|
1827
|
+
cleanup();
|
|
1828
|
+
observer.error(this.buildHttpError("Kết nối đến máy chủ hết thời gian. Vui lòng thử lại.", {
|
|
1829
|
+
status: xhr?.status,
|
|
1830
|
+
statusText: xhr?.statusText,
|
|
1831
|
+
url: presignedURL,
|
|
1832
|
+
}));
|
|
1833
|
+
};
|
|
1834
|
+
xhr.upload.addEventListener("progress", progressHandler);
|
|
1835
|
+
xhr.addEventListener("load", loadHandler);
|
|
1836
|
+
xhr.addEventListener("error", errorHandler);
|
|
1837
|
+
xhr.addEventListener("timeout", timeoutHandler);
|
|
1838
|
+
xhr.timeout = 60000 * 5;
|
|
1839
|
+
xhr.open("PUT", presignedURL, true);
|
|
1840
|
+
xhr.setRequestHeader("Content-Type", contentType);
|
|
1841
|
+
xhr.send(file);
|
|
1842
|
+
})
|
|
1843
|
+
.catch((error) => {
|
|
1844
|
+
if (xhr) {
|
|
1845
|
+
xhr.abort();
|
|
1846
|
+
xhr = null;
|
|
1847
|
+
}
|
|
1848
|
+
let userMessage = "Upload file thất bại";
|
|
1849
|
+
if (error.errorMessage) {
|
|
1850
|
+
userMessage = error.errorMessage;
|
|
1851
|
+
}
|
|
1852
|
+
else if (error.code === "ECONNABORTED" ||
|
|
1853
|
+
error.message?.includes("timeout")) {
|
|
1854
|
+
userMessage =
|
|
1855
|
+
"Kết nối đến máy chủ hết thời gian. Vui lòng thử lại.";
|
|
1856
|
+
}
|
|
1857
|
+
else if (error.message?.includes("failed to respond") ||
|
|
1858
|
+
error.message?.includes("Network Error")) {
|
|
1859
|
+
userMessage =
|
|
1860
|
+
"Không thể kết nối đến máy chủ lưu trữ. Máy chủ không phản hồi.";
|
|
1861
|
+
}
|
|
1862
|
+
else if (error.message) {
|
|
1863
|
+
userMessage = `Lỗi kết nối: ${error.message}`;
|
|
1864
|
+
}
|
|
1865
|
+
else if (error.error?.message) {
|
|
1866
|
+
userMessage = error.error.message;
|
|
1867
|
+
}
|
|
1868
|
+
observer.error(this.buildHttpError(userMessage, {
|
|
1869
|
+
status: error?.status,
|
|
1870
|
+
statusText: error?.statusText,
|
|
1871
|
+
url: error?.url,
|
|
1872
|
+
cause: error,
|
|
1873
|
+
}));
|
|
1874
|
+
});
|
|
1875
|
+
return () => {
|
|
1876
|
+
if (xhr) {
|
|
1877
|
+
xhr.abort();
|
|
1878
|
+
xhr = null;
|
|
1879
|
+
}
|
|
1880
|
+
};
|
|
1881
|
+
});
|
|
1882
|
+
}
|
|
1883
|
+
/**
|
|
1884
|
+
* Tạo tên file unique bằng cách thêm timestamp và random string
|
|
1885
|
+
* Format: originalname-timestamp-random.ext
|
|
1886
|
+
*/
|
|
1887
|
+
generateUniqueFileName(originalFileName) {
|
|
1888
|
+
const timestamp = Date.now();
|
|
1889
|
+
const randomString = Math.random().toString(36).substring(2, 9);
|
|
1890
|
+
// Tách tên file và extension
|
|
1891
|
+
const lastDotIndex = originalFileName.lastIndexOf(".");
|
|
1892
|
+
if (lastDotIndex === -1) {
|
|
1893
|
+
// Không có extension
|
|
1894
|
+
return `${originalFileName}-${timestamp}-${randomString}`;
|
|
1895
|
+
}
|
|
1896
|
+
const nameWithoutExt = originalFileName.substring(0, lastDotIndex);
|
|
1897
|
+
const extension = originalFileName.substring(lastDotIndex);
|
|
1898
|
+
return `${nameWithoutExt}-${timestamp}-${randomString}${extension}`;
|
|
1899
|
+
}
|
|
1900
|
+
extractFileName(filePath) {
|
|
1901
|
+
return filePath.split("/").pop() || "";
|
|
1902
|
+
}
|
|
1903
|
+
buildResult(file, filePath, url) {
|
|
1904
|
+
return {
|
|
1905
|
+
url,
|
|
1906
|
+
filePath,
|
|
1907
|
+
fileName: this.extractFileName(filePath) || file.name,
|
|
1908
|
+
originalName: file.name,
|
|
1909
|
+
};
|
|
1910
|
+
}
|
|
1708
1911
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FileGCSService, deps: [{ token: i1$2.HttpClient }, { token: ApiService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1709
1912
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FileGCSService, providedIn: "root" }); }
|
|
1710
1913
|
}
|
|
@@ -1722,6 +1925,9 @@ class FileAIA_GCSService {
|
|
|
1722
1925
|
getSignedLink(filePath) {
|
|
1723
1926
|
return this._apiService.post(`${this.baseUrl}/getSignedLink?filePath=${filePath}`);
|
|
1724
1927
|
}
|
|
1928
|
+
getPresignedLink(fileName) {
|
|
1929
|
+
return this._apiService.post(`${this.baseUrl}/getPresignedLink?fileName=${fileName}`);
|
|
1930
|
+
}
|
|
1725
1931
|
attachFile(file) {
|
|
1726
1932
|
const formData = new FormData();
|
|
1727
1933
|
formData.append("file", file);
|
|
@@ -1740,6 +1946,206 @@ class FileAIA_GCSService {
|
|
|
1740
1946
|
});
|
|
1741
1947
|
return firstValueFrom(this._http.request(req));
|
|
1742
1948
|
}
|
|
1949
|
+
/**
|
|
1950
|
+
* Build an Error object but keep HTTP-related metadata for callers that need to branch by status.
|
|
1951
|
+
* Note: We intentionally do NOT change business flow; we only enrich the error instance.
|
|
1952
|
+
*/
|
|
1953
|
+
buildHttpError(message, meta) {
|
|
1954
|
+
const err = new Error(message);
|
|
1955
|
+
if (meta?.status !== undefined)
|
|
1956
|
+
err.status = meta.status;
|
|
1957
|
+
if (meta?.statusText !== undefined)
|
|
1958
|
+
err.statusText = meta.statusText;
|
|
1959
|
+
if (meta?.url !== undefined)
|
|
1960
|
+
err.url = meta.url;
|
|
1961
|
+
if (meta?.cause !== undefined)
|
|
1962
|
+
err.cause = meta.cause;
|
|
1963
|
+
return err;
|
|
1964
|
+
}
|
|
1965
|
+
uploadFile(file) {
|
|
1966
|
+
return new Observable((observer) => {
|
|
1967
|
+
let xhr = null;
|
|
1968
|
+
// Tạo tên file unique để tránh trùng tên khi upload nhiều lần
|
|
1969
|
+
const uniqueFileName = this.generateUniqueFileName(file.name);
|
|
1970
|
+
firstValueFrom(this.getPresignedLink(uniqueFileName))
|
|
1971
|
+
.then((presignedData) => {
|
|
1972
|
+
if (!presignedData) {
|
|
1973
|
+
throw new Error("Không nhận được presigned link từ server");
|
|
1974
|
+
}
|
|
1975
|
+
const presignedURL = presignedData.PreSignedURL ||
|
|
1976
|
+
presignedData.preSignedURL ||
|
|
1977
|
+
presignedData.url;
|
|
1978
|
+
const filePath = presignedData.FilePath || presignedData.filePath;
|
|
1979
|
+
const absoluteURL = presignedData.AbsoluteURL || presignedData.absoluteURL;
|
|
1980
|
+
if (!presignedURL) {
|
|
1981
|
+
throw new Error("Presigned URL không hợp lệ");
|
|
1982
|
+
}
|
|
1983
|
+
const contentType = presignedData.ContentType ||
|
|
1984
|
+
presignedData.contentType ||
|
|
1985
|
+
file.type ||
|
|
1986
|
+
"application/octet-stream";
|
|
1987
|
+
xhr = new XMLHttpRequest();
|
|
1988
|
+
const cleanup = () => {
|
|
1989
|
+
if (xhr) {
|
|
1990
|
+
xhr.upload.removeEventListener("progress", progressHandler);
|
|
1991
|
+
xhr.removeEventListener("load", loadHandler);
|
|
1992
|
+
xhr.removeEventListener("error", errorHandler);
|
|
1993
|
+
xhr.removeEventListener("timeout", timeoutHandler);
|
|
1994
|
+
xhr.abort();
|
|
1995
|
+
xhr = null;
|
|
1996
|
+
}
|
|
1997
|
+
};
|
|
1998
|
+
const progressHandler = (event) => {
|
|
1999
|
+
if (event.lengthComputable) {
|
|
2000
|
+
observer.next({
|
|
2001
|
+
type: HttpEventType.UploadProgress,
|
|
2002
|
+
loaded: event.loaded,
|
|
2003
|
+
total: event.total,
|
|
2004
|
+
});
|
|
2005
|
+
}
|
|
2006
|
+
};
|
|
2007
|
+
const loadHandler = async () => {
|
|
2008
|
+
if (xhr && xhr.status === 200) {
|
|
2009
|
+
try {
|
|
2010
|
+
const finalUrl = absoluteURL || presignedURL;
|
|
2011
|
+
const result = this.buildResult(file, filePath, finalUrl);
|
|
2012
|
+
cleanup();
|
|
2013
|
+
observer.next(new HttpResponse({
|
|
2014
|
+
body: result,
|
|
2015
|
+
status: 200,
|
|
2016
|
+
statusText: "OK",
|
|
2017
|
+
url: presignedURL,
|
|
2018
|
+
}));
|
|
2019
|
+
observer.complete();
|
|
2020
|
+
}
|
|
2021
|
+
catch (err) {
|
|
2022
|
+
cleanup();
|
|
2023
|
+
const msg = err?.message ||
|
|
2024
|
+
"Upload thất bại. Vui lòng thử lại.";
|
|
2025
|
+
observer.error(this.buildHttpError(msg, {
|
|
2026
|
+
status: xhr?.status,
|
|
2027
|
+
statusText: xhr?.statusText,
|
|
2028
|
+
url: presignedURL,
|
|
2029
|
+
cause: err,
|
|
2030
|
+
}));
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
else if (xhr) {
|
|
2034
|
+
const errorMessage = `Upload file thất bại: ${xhr.statusText} (${xhr.status})`;
|
|
2035
|
+
cleanup();
|
|
2036
|
+
observer.error(this.buildHttpError(errorMessage, {
|
|
2037
|
+
status: xhr.status,
|
|
2038
|
+
statusText: xhr.statusText,
|
|
2039
|
+
url: presignedURL,
|
|
2040
|
+
}));
|
|
2041
|
+
}
|
|
2042
|
+
};
|
|
2043
|
+
const errorHandler = () => {
|
|
2044
|
+
if (!xhr)
|
|
2045
|
+
return;
|
|
2046
|
+
let userMessage = "Upload file thất bại";
|
|
2047
|
+
if (xhr.status === 0) {
|
|
2048
|
+
userMessage =
|
|
2049
|
+
"Không thể kết nối đến máy chủ lưu trữ. Máy chủ không phản hồi.";
|
|
2050
|
+
}
|
|
2051
|
+
else if (xhr.status >= 500) {
|
|
2052
|
+
userMessage = "Lỗi máy chủ. Vui lòng thử lại sau.";
|
|
2053
|
+
}
|
|
2054
|
+
else if (xhr.status >= 400) {
|
|
2055
|
+
userMessage = `Lỗi yêu cầu: ${xhr.statusText}`;
|
|
2056
|
+
}
|
|
2057
|
+
cleanup();
|
|
2058
|
+
observer.error(this.buildHttpError(userMessage, {
|
|
2059
|
+
status: xhr.status,
|
|
2060
|
+
statusText: xhr.statusText,
|
|
2061
|
+
url: presignedURL,
|
|
2062
|
+
}));
|
|
2063
|
+
};
|
|
2064
|
+
const timeoutHandler = () => {
|
|
2065
|
+
cleanup();
|
|
2066
|
+
observer.error(this.buildHttpError("Kết nối đến máy chủ hết thời gian. Vui lòng thử lại.", {
|
|
2067
|
+
status: xhr?.status,
|
|
2068
|
+
statusText: xhr?.statusText,
|
|
2069
|
+
url: presignedURL,
|
|
2070
|
+
}));
|
|
2071
|
+
};
|
|
2072
|
+
xhr.upload.addEventListener("progress", progressHandler);
|
|
2073
|
+
xhr.addEventListener("load", loadHandler);
|
|
2074
|
+
xhr.addEventListener("error", errorHandler);
|
|
2075
|
+
xhr.addEventListener("timeout", timeoutHandler);
|
|
2076
|
+
xhr.timeout = 60000 * 5;
|
|
2077
|
+
xhr.open("PUT", presignedURL, true);
|
|
2078
|
+
xhr.setRequestHeader("Content-Type", contentType);
|
|
2079
|
+
xhr.send(file);
|
|
2080
|
+
})
|
|
2081
|
+
.catch((error) => {
|
|
2082
|
+
if (xhr) {
|
|
2083
|
+
xhr.abort();
|
|
2084
|
+
xhr = null;
|
|
2085
|
+
}
|
|
2086
|
+
let userMessage = "Upload file thất bại";
|
|
2087
|
+
if (error.errorMessage) {
|
|
2088
|
+
userMessage = error.errorMessage;
|
|
2089
|
+
}
|
|
2090
|
+
else if (error.code === "ECONNABORTED" ||
|
|
2091
|
+
error.message?.includes("timeout")) {
|
|
2092
|
+
userMessage =
|
|
2093
|
+
"Kết nối đến máy chủ hết thời gian. Vui lòng thử lại.";
|
|
2094
|
+
}
|
|
2095
|
+
else if (error.message?.includes("failed to respond") ||
|
|
2096
|
+
error.message?.includes("Network Error")) {
|
|
2097
|
+
userMessage =
|
|
2098
|
+
"Không thể kết nối đến máy chủ lưu trữ. Máy chủ không phản hồi.";
|
|
2099
|
+
}
|
|
2100
|
+
else if (error.message) {
|
|
2101
|
+
userMessage = `Lỗi kết nối: ${error.message}`;
|
|
2102
|
+
}
|
|
2103
|
+
else if (error.error?.message) {
|
|
2104
|
+
userMessage = error.error.message;
|
|
2105
|
+
}
|
|
2106
|
+
observer.error(this.buildHttpError(userMessage, {
|
|
2107
|
+
status: error?.status,
|
|
2108
|
+
statusText: error?.statusText,
|
|
2109
|
+
url: error?.url,
|
|
2110
|
+
cause: error,
|
|
2111
|
+
}));
|
|
2112
|
+
});
|
|
2113
|
+
return () => {
|
|
2114
|
+
if (xhr) {
|
|
2115
|
+
xhr.abort();
|
|
2116
|
+
xhr = null;
|
|
2117
|
+
}
|
|
2118
|
+
};
|
|
2119
|
+
});
|
|
2120
|
+
}
|
|
2121
|
+
/**
|
|
2122
|
+
* Tạo tên file unique bằng cách thêm timestamp và random string
|
|
2123
|
+
* Format: originalname-timestamp-random.ext
|
|
2124
|
+
*/
|
|
2125
|
+
generateUniqueFileName(originalFileName) {
|
|
2126
|
+
const timestamp = Date.now();
|
|
2127
|
+
const randomString = Math.random().toString(36).substring(2, 9);
|
|
2128
|
+
// Tách tên file và extension
|
|
2129
|
+
const lastDotIndex = originalFileName.lastIndexOf(".");
|
|
2130
|
+
if (lastDotIndex === -1) {
|
|
2131
|
+
// Không có extension
|
|
2132
|
+
return `${originalFileName}-${timestamp}-${randomString}`;
|
|
2133
|
+
}
|
|
2134
|
+
const nameWithoutExt = originalFileName.substring(0, lastDotIndex);
|
|
2135
|
+
const extension = originalFileName.substring(lastDotIndex);
|
|
2136
|
+
return `${nameWithoutExt}-${timestamp}-${randomString}${extension}`;
|
|
2137
|
+
}
|
|
2138
|
+
extractFileName(filePath) {
|
|
2139
|
+
return filePath.split("/").pop() || "";
|
|
2140
|
+
}
|
|
2141
|
+
buildResult(file, filePath, url) {
|
|
2142
|
+
return {
|
|
2143
|
+
url,
|
|
2144
|
+
filePath,
|
|
2145
|
+
fileName: this.extractFileName(filePath) || file.name,
|
|
2146
|
+
originalName: file.name,
|
|
2147
|
+
};
|
|
2148
|
+
}
|
|
1743
2149
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FileAIA_GCSService, deps: [{ token: i1$2.HttpClient }, { token: ApiService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1744
2150
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FileAIA_GCSService, providedIn: "root" }); }
|
|
1745
2151
|
}
|
|
@@ -1784,7 +2190,7 @@ class FileWAPGCSService {
|
|
|
1784
2190
|
formData.append("file", file);
|
|
1785
2191
|
const req = new HttpRequest("POST", `${this.baseUrl}/attachFile`, formData, {
|
|
1786
2192
|
reportProgress: true,
|
|
1787
|
-
responseType: "json"
|
|
2193
|
+
responseType: "json",
|
|
1788
2194
|
});
|
|
1789
2195
|
return this._http.request(req);
|
|
1790
2196
|
}
|
|
@@ -1793,17 +2199,17 @@ class FileWAPGCSService {
|
|
|
1793
2199
|
formData.append("file", file);
|
|
1794
2200
|
const req = new HttpRequest("POST", `${this.baseUrl}/attachFile`, formData, {
|
|
1795
2201
|
reportProgress: true,
|
|
1796
|
-
responseType: "json"
|
|
2202
|
+
responseType: "json",
|
|
1797
2203
|
});
|
|
1798
2204
|
return firstValueFrom(this._http.request(req));
|
|
1799
2205
|
}
|
|
1800
2206
|
uploadFile(file) {
|
|
1801
|
-
return new Observable(observer => {
|
|
2207
|
+
return new Observable((observer) => {
|
|
1802
2208
|
let xhr = null;
|
|
1803
2209
|
// Tạo tên file unique để tránh trùng tên khi upload nhiều lần
|
|
1804
2210
|
const uniqueFileName = this.generateUniqueFileName(file.name);
|
|
1805
2211
|
firstValueFrom(this.getPresignedLink(uniqueFileName))
|
|
1806
|
-
.then(presignedData => {
|
|
2212
|
+
.then((presignedData) => {
|
|
1807
2213
|
if (!presignedData) {
|
|
1808
2214
|
throw new Error("Không nhận được presigned link từ server");
|
|
1809
2215
|
}
|
|
@@ -1815,7 +2221,10 @@ class FileWAPGCSService {
|
|
|
1815
2221
|
if (!presignedURL) {
|
|
1816
2222
|
throw new Error("Presigned URL không hợp lệ");
|
|
1817
2223
|
}
|
|
1818
|
-
const contentType =
|
|
2224
|
+
const contentType = presignedData.ContentType ||
|
|
2225
|
+
presignedData.contentType ||
|
|
2226
|
+
file.type ||
|
|
2227
|
+
"application/octet-stream";
|
|
1819
2228
|
xhr = new XMLHttpRequest();
|
|
1820
2229
|
const cleanup = () => {
|
|
1821
2230
|
if (xhr) {
|
|
@@ -1827,8 +2236,14 @@ class FileWAPGCSService {
|
|
|
1827
2236
|
xhr = null;
|
|
1828
2237
|
}
|
|
1829
2238
|
};
|
|
1830
|
-
const progressHandler = (
|
|
1831
|
-
|
|
2239
|
+
const progressHandler = (event) => {
|
|
2240
|
+
if (event.lengthComputable) {
|
|
2241
|
+
observer.next({
|
|
2242
|
+
type: HttpEventType.UploadProgress,
|
|
2243
|
+
loaded: event.loaded,
|
|
2244
|
+
total: event.total,
|
|
2245
|
+
});
|
|
2246
|
+
}
|
|
1832
2247
|
};
|
|
1833
2248
|
const loadHandler = async () => {
|
|
1834
2249
|
if (xhr && xhr.status === 200) {
|
|
@@ -1842,7 +2257,12 @@ class FileWAPGCSService {
|
|
|
1842
2257
|
const finalUrl = absoluteURL || presignedURL;
|
|
1843
2258
|
const result = this.buildResult(file, filePath, finalUrl);
|
|
1844
2259
|
cleanup();
|
|
1845
|
-
observer.next(
|
|
2260
|
+
observer.next(new HttpResponse({
|
|
2261
|
+
body: result,
|
|
2262
|
+
status: 200,
|
|
2263
|
+
statusText: "OK",
|
|
2264
|
+
url: presignedURL,
|
|
2265
|
+
}));
|
|
1846
2266
|
observer.complete();
|
|
1847
2267
|
}
|
|
1848
2268
|
catch (err) {
|
|
@@ -1853,7 +2273,7 @@ class FileWAPGCSService {
|
|
|
1853
2273
|
status: xhr?.status,
|
|
1854
2274
|
statusText: xhr?.statusText,
|
|
1855
2275
|
url: presignedURL,
|
|
1856
|
-
cause: err
|
|
2276
|
+
cause: err,
|
|
1857
2277
|
}));
|
|
1858
2278
|
}
|
|
1859
2279
|
}
|
|
@@ -1863,7 +2283,7 @@ class FileWAPGCSService {
|
|
|
1863
2283
|
observer.error(this.buildHttpError(errorMessage, {
|
|
1864
2284
|
status: xhr.status,
|
|
1865
2285
|
statusText: xhr.statusText,
|
|
1866
|
-
url: presignedURL
|
|
2286
|
+
url: presignedURL,
|
|
1867
2287
|
}));
|
|
1868
2288
|
}
|
|
1869
2289
|
};
|
|
@@ -1885,7 +2305,7 @@ class FileWAPGCSService {
|
|
|
1885
2305
|
observer.error(this.buildHttpError(userMessage, {
|
|
1886
2306
|
status: xhr.status,
|
|
1887
2307
|
statusText: xhr.statusText,
|
|
1888
|
-
url: presignedURL
|
|
2308
|
+
url: presignedURL,
|
|
1889
2309
|
}));
|
|
1890
2310
|
};
|
|
1891
2311
|
const timeoutHandler = () => {
|
|
@@ -1893,7 +2313,7 @@ class FileWAPGCSService {
|
|
|
1893
2313
|
observer.error(this.buildHttpError("Kết nối đến máy chủ hết thời gian. Vui lòng thử lại.", {
|
|
1894
2314
|
status: xhr?.status,
|
|
1895
2315
|
statusText: xhr?.statusText,
|
|
1896
|
-
url: presignedURL
|
|
2316
|
+
url: presignedURL,
|
|
1897
2317
|
}));
|
|
1898
2318
|
};
|
|
1899
2319
|
xhr.upload.addEventListener("progress", progressHandler);
|
|
@@ -1905,7 +2325,7 @@ class FileWAPGCSService {
|
|
|
1905
2325
|
xhr.setRequestHeader("Content-Type", contentType);
|
|
1906
2326
|
xhr.send(file);
|
|
1907
2327
|
})
|
|
1908
|
-
.catch(error => {
|
|
2328
|
+
.catch((error) => {
|
|
1909
2329
|
if (xhr) {
|
|
1910
2330
|
xhr.abort();
|
|
1911
2331
|
xhr = null;
|
|
@@ -1935,7 +2355,7 @@ class FileWAPGCSService {
|
|
|
1935
2355
|
status: error?.status,
|
|
1936
2356
|
statusText: error?.statusText,
|
|
1937
2357
|
url: error?.url,
|
|
1938
|
-
cause: error
|
|
2358
|
+
cause: error,
|
|
1939
2359
|
}));
|
|
1940
2360
|
});
|
|
1941
2361
|
return () => {
|
|
@@ -1971,7 +2391,7 @@ class FileWAPGCSService {
|
|
|
1971
2391
|
url,
|
|
1972
2392
|
filePath,
|
|
1973
2393
|
fileName: this.extractFileName(filePath) || file.name,
|
|
1974
|
-
originalName: file.name
|
|
2394
|
+
originalName: file.name,
|
|
1975
2395
|
};
|
|
1976
2396
|
}
|
|
1977
2397
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FileWAPGCSService, deps: [{ token: i1$2.HttpClient }, { token: ApiService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|