Chào mọi người.
Một hồi lay hoay tìm hiểu về phương thứcupload image lấy link trực tiếp thì mình thấy có bài bên truongdevs.com có chia sẽ việc upload image lên Cloudinary và lấy link trực tiếp nên mình mang về chia sẽ cho mọi người sữ dụng.Và việc
upload image hình ảnh từ blogger thì có nhiều bài viết chia sẽ về việc phát triển bằng việc upload imgur rồi.Này mình chia sẽ tiếp cho mọi người trang
Cloudinary để upload image để không bị phụ thuộc vào imgur như lúc xưa nhé.Chúng ta bắt đầu.
Cloudinary là gì? Vì sao dùng nó để up ảnh lấy link?
Cloudinary là dịch vụ lưu trữ ảnh và video chuyên nghiệp, cho phép upload trực tiếp từ trình duyệt và nhận link chuẩn HTTPS. Ưu điểm: tốc độ nhanh, link bền vững, hỗ trợ tối ưuf_auto,q_auto để ảnh vừa nhẹ vừa nét. Dùng gói free thôi cũng dư sức cho nhu cầu blog cá nhân hay website học tập. Đây là lý do nhiều anh em IT chọn Cloudinary để thay thế Imgur, Google Drive hay Dropbox khi cần up ảnh lấy link.Chuẩn bị tài khoản Cloudinary trước khi bắt tay vào code
Để code chạy được, anh em cần tạo tài khoảnCloudinary. Làm vài bước là xong:Bước 1. Mọi người vào trang Cloudinary đăng ký gói miễn phí.
Bước 2. Vào Dashboard, lấy Cloud Name.
Bước 3. Trong phần Settings → Upload → Upload Presets, tạo một preset dạng Unsigned.
Bước 4. Lưu lại tên preset đó.
Mọi người lưu ý.Unsigned uploadnghĩa là bất kỳ ai cóCloud NamevàPreset Nameđều up được ảnh, và ảnh sẽ lưu vào tài khoảnCloudinarycủa bạn. Nếu để public trên blog, hãy cẩn thận với nội dung upload.
Tạo trang up ảnh lấy link nhanh
Sau khi có Cloud Name và Upload Preset, chúng ta tiến hành dựng ngay trang upload ảnh. Cách làm đơn giản: tạo một Page mới trong Blogger, chuyển sang chế độ HTML và dán đoạn code dưới đây. Trang này sẽ có khung kéo-thả, nút copy link, và cả preview ảnh đã upload.
<div class="up-i">
<div class="dropzone">
<div class="infoimg" id="svgZ">
<svg enable-background="new 0 0 256 256" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg"><g><g><g>
<path d="M127.7,31.6c-7.2,0.9-18.7,4.6-25.3,8.2c-13.6,7.6-26.2,21.9-31.1,35.5L70,79h-4.1c-5.6,0-15.1,2-20.8,4.4
c-15.1,6.3-27.5,19.8-32.8,35.6c-1.9,5.7-2.1,7.3-2.2,17.9c0,10.8,0.2,12.1,2.2,17.9c6.5,18.7,20.7,32.2,39.6,37.8
c5.1,1.6,8,1.7,27.9,2c21.9,0.4,22.3,0.4,24-1.2c2.3-2.3,2.2-6.6-0.1-8.2c-1.5-1.1-4.9-1.2-21.2-1.2c-22.2,0-26.7-0.7-36-5.1
c-4.7-2.3-7.4-4.3-12.2-9.2c-9.6-9.8-13.6-19.4-13.6-32.7c0-13.5,3.9-22.9,13.7-32.8c9.9-10.1,20.1-14.2,35.2-14.2
c3.7,0,7.2-0.3,7.7-0.6c0.5-0.4,2-3.5,3.3-7.1C84.4,71,91,61.6,99.9,54.5c24.5-19.1,58.7-16.2,79.7,6.8c9,9.8,13.9,21.1,15,34.6
c0.8,8.7,1.5,9.6,8.6,10.9c13.7,2.4,25.8,13,30.3,26.4c1.8,5.3,2.2,15.2,0.8,21.1c-2.1,9.2-10.7,20.3-19.2,24.8
c-8.1,4.3-12,4.8-38,4.8c-21.6,0-23.6,0.1-25.2,1.4c-2.3,1.9-2.3,6.2,0.1,8.1c1.5,1.2,3.9,1.4,24.8,1.4c17.3,0,24.7-0.3,29.3-1.2
c18.4-3.5,34.4-18.7,38.7-36.9c1.6-6.5,1.6-16.9,0-23.3c-3.9-16.6-18-31.3-34.5-36.1l-4.4-1.2l-1.2-7.2
C198.5,52.5,164.8,27.5,127.7,31.6z"></path>
<path d="M126.3,134c-0.6,0.2-7.9,7.1-16.1,15.3c-12.9,12.8-15,15.2-15,17.2c0,1.2,0.3,2.7,0.6,3.2c1,1.6,4.1,2.7,5.8,2.3
c0.9-0.3,6-4.8,11.3-10l9.5-9.5v34.4c0,32.1,0.1,34.4,1.4,36.1c2,2.4,6.1,2.4,8.1,0c1.3-1.7,1.4-4,1.4-36.1v-34.4l9.6,9.5
c5.2,5.1,10.3,9.6,11.2,10c1.7,0.5,4.8-0.7,5.8-2.3c0.3-0.5,0.6-1.9,0.6-3.2c0-2-2.1-4.4-14.9-17.3c-8.2-8.2-15.7-15-16.6-15.2
C128.2,133.8,126.9,133.8,126.3,134z"></path></g></g></g>
</svg>
<p><b>Chọn file ảnh</b> hoặc kéo ảnh vào đây (nhiều file)</p>
</div>
<input accept="image/*" class="input" type="file" multiple />
</div>
<div class="actions hidden" aria-live="polite">
<button type="button" class="btn btn-primary copy-all-btn" disabled>Copy tất cả link</button>
<button type="button" class="btn btn-ghost clear-list-btn" disabled>Xoá danh sách</button>
</div>
<div class="results"></div>
<div id="toast" class="toast"></div>
</div>
<div class="loading-modal"><div class="loading-spinner"></div></div>
<style>
.up-i { background: var(--contentBg-alt); padding: 10px; border-radius: 5px; }
.dropzone { border: 1px dashed #999; position: relative; margin: 0 auto; clear: both; }
.dropzone.dropzone-dragging { border-color: #000; }
#svgZ { text-align:center; }
#svgZ svg { width:79px; height:79px; }
.input { height: 100%; left: 0; outline: 0; opacity: 0; position: absolute; top: 0; width: 100%; cursor: cell; }
.actions { display:flex; gap:10px; justify-content:center; margin:12px 0; }
.actions.hidden { display:none; }
.copy-all-btn:disabled, .clear-list-btn:disabled { opacity:.6; cursor:not-allowed; }
.actions .btn { all:unset; display:inline-flex; align-items:center; justify-content:center; padding:8px 12px; border-radius:8px; line-height:1; border:1px solid transparent; cursor:pointer; user-select:none; -webkit-user-select:none; }
.actions .btn[disabled] { opacity:.5; cursor:not-allowed; }
.actions .btn-primary { background: var(--linkB, #2563eb); color:#fff; }
.actions .btn-primary:hover { filter:brightness(.95); }
.actions .btn-ghost { background:#f3f4f6; color:#111827; border-color:#e5e7eb; }
.actions .btn-ghost:hover { background:#e5e7eb; }
.results { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 14px; }
.card { border:1px solid var(--bodyCa); border-radius:10px; padding:10px; background:#fff; display:flex; flex-direction:column; }
.thumb { display:flex; align-items:center; justify-content:center; min-height:160px; }
.thumb img { max-width:100%; max-height:240px; height:auto; border-radius:6px; }
.link-row { display:flex; gap:8px; margin-top:auto; align-items:center; }
.image-url { flex:1; min-width:0; background:transparent; color:#08102b; border:1px solid var(--bodyCa); padding:6px 8px; border-radius:6px; }
.copy-btn { padding: 6px 10px; background: var(--linkB); color:#fff; border:none; border-radius:6px; cursor:pointer; }
.toast { position: fixed; top: 30px; right: 30px; background-color: #22c55e; color: white; padding: 12px 24px; border-radius: 6px; font-size: 14px; opacity: 0; pointer-events: none; transition: opacity 0.4s ease, transform 0.4s ease; z-index: 9999; box-shadow: 0 6px 24px rgba(0,0,0,.12); transform: translateY(0); }
.toast.error { background-color:#ef4444; }
.toast.show { opacity:1; transform: translateY(4px); }
.toast.warn { background-color:#f59e0b; }
body.loading .loading-modal { display:flex; }
.loading-modal { background-color: rgba(255,255,255,.8); display:none; position:fixed; z-index:1000; inset:0; justify-content:center; align-items:center; }
.loading-spinner { border:6px solid #f3f3f3; border-top:6px solid #3f51b5; border-radius:50%; width:50px; height:50px; animation: spin 1s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
</style>
<script>
const CLOUD_NAME = "domvpkjum";
const UPLOAD_PRESET = "truongdevs";
const ACCEPT_TYPES = ["image/jpeg", "image/png", "image/gif"];
const MAX_SIZE = 5 * 1024 * 1024;
const MAX_FILES_PER_BATCH = 20;
function showToast(message, variant = "success") {
const toast = document.getElementById("toast");
toast.textContent = message;
toast.classList.remove("error", "warn");
if (variant === "error") toast.classList.add("error");
if (variant === "warn") toast.classList.add("warn");
toast.classList.add("show");
setTimeout(() => {
toast.classList.remove("show", "error", "warn");
}, 2500);
}
function createLoading() {
if (!document.querySelector(".loading-modal")) {
const div = document.createElement("div");
div.className = "loading-modal";
const spinner = document.createElement("div");
spinner.className = "loading-spinner";
div.appendChild(spinner);
document.body.appendChild(div);
}
}
async function uploadToCloudinary(file) {
const endpoint = `https://api.cloudinary.com/v1_1/${CLOUD_NAME}/image/upload`;
const formData = new FormData();
formData.append("file", file);
formData.append("upload_preset", UPLOAD_PRESET);
const res = await fetch(endpoint, { method: "POST", body: formData });
if (!res.ok) {
const text = await res.text();
throw new Error(text || (res.status + " " + res.statusText));
}
return await res.json();
}
function optimizedUrl(secureUrl) {
return secureUrl.replace("/upload/", "/upload/f_auto,q_auto/");
}
function copyText(text) {
return navigator.clipboard.writeText(text)
.then(() => showToast("Đã sao chép!"))
.catch(() => showToast("Không thể sao chép tự động.", "error"));
}
function clampFiles(files) {
const arr = Array.from(files);
if (arr.length > MAX_FILES_PER_BATCH) {
showToast(
`Chỉ xử lý tối đa ${MAX_FILES_PER_BATCH} ảnh/lần. Đã bỏ qua ${arr.length - MAX_FILES_PER_BATCH} ảnh.`,
"error"
);
return arr.slice(0, MAX_FILES_PER_BATCH);
}
return arr;
}
function createCard({ url }) {
const card = document.createElement("div");
card.className = "card";
const thumb = document.createElement("div");
thumb.className = "thumb";
thumb.innerHTML = `<img alt="Cloudinary-Upload" src="${url}">`;
const linkRow = document.createElement("div");
linkRow.className = "link-row";
const input = document.createElement("input");
input.className = "image-url";
input.readOnly = true;
input.value = url;
input.addEventListener("focus", () => input.select());
const btn = document.createElement("button");
btn.className = "copy-btn";
btn.type = "button";
btn.textContent = "Copy";
btn.addEventListener("click", () => copyText(url));
linkRow.appendChild(input);
linkRow.appendChild(btn);
card.appendChild(thumb);
card.appendChild(linkRow);
return card;
}
(function () {
const dropzone = document.querySelector(".dropzone");
const fileInput = dropzone.querySelector("input.input");
const results = document.querySelector(".results");
const actions = document.querySelector(".actions");
const copyAllBtn = document.querySelector(".copy-all-btn");
const clearListBtn = document.querySelector(".clear-list-btn");
let uploadingCount = 0;
createLoading();
function setActionsVisibleIfHasResults() {
const hasResults = !!results.querySelector(".card");
actions.classList.toggle("hidden", !hasResults);
}
function setActionsEnabled(enabled) {
copyAllBtn.disabled = !enabled;
clearListBtn.disabled = !enabled;
}
["dragenter", "dragover"].forEach(ev => {
dropzone.addEventListener(ev, e => {
e.preventDefault();
e.stopPropagation();
dropzone.classList.add("dropzone-dragging");
});
});
["dragleave", "drop"].forEach(ev => {
dropzone.addEventListener(ev, e => {
e.preventDefault();
e.stopPropagation();
dropzone.classList.remove("dropzone-dragging");
});
});
dropzone.addEventListener("drop", e => {
const dt = e.dataTransfer;
const files = clampFiles((dt && dt.files) || []);
if (!files.length) return;
multiHandleFiles(files);
});
fileInput.addEventListener("change", (e) => {
const files = clampFiles(e.target.files || []);
if (!files.length) return;
multiHandleFiles(files);
e.target.value = "";
});
async function multiHandleFiles(files) {
const valid = [];
for (const file of files) {
if (!ACCEPT_TYPES.includes(file.type)) {
showToast("Bỏ qua file không hỗ trợ (chỉ JPG/PNG/GIF).", "error");
continue;
}
if (file.size > MAX_SIZE) {
showToast("Bỏ qua file > 5MB.", "error");
continue;
}
valid.push(file);
}
if (!valid.length) return;
uploadingCount += valid.length;
document.body.classList.add("loading");
setActionsVisibleIfHasResults();
setActionsEnabled(false);
try {
const uploads = await Promise.allSettled(valid.map(f => uploadToCloudinary(f)));
let successCount = 0;
uploads.forEach((res, idx) => {
if (res.status === "fulfilled") {
const url = optimizedUrl(res.value.secure_url);
results.prepend(createCard({ url }));
successCount++;
} else {
console.warn("Upload failed:", valid[idx]?.name, res.reason);
showToast(`1 file lỗi: ${valid[idx]?.name || ""}`, "error");
}
});
if (successCount) {
showToast(`Đã upload ${successCount} ảnh`);
setActionsVisibleIfHasResults();
}
} finally {
uploadingCount -= valid.length;
if (uploadingCount <= 0) {
document.body.classList.remove("loading");
const hasResults = !!results.querySelector(".card");
setActionsEnabled(hasResults);
}
}
}
copyAllBtn.addEventListener("click", () => {
const inputs = results.querySelectorAll(".image-url");
if (!inputs.length) return showToast("Chưa có link để copy.", "error");
const all = Array.from(inputs).map(i => i.value).join("\n");
copyText(all);
});
clearListBtn.addEventListener("click", () => {
results.innerHTML = "";
setActionsVisibleIfHasResults();
setActionsEnabled(false);
showToast("Đã xoá danh sách.", "warn");
});
})();
</script>
Những thành phần đánh dấu bạn có quyền có/không thay đổi sao cho phù hợp với nhu cầu. Tuy nhiên phần
CLOUD_NAME và UPLOAD_PRESET bắt buộc thay đổi để code hoạt động.Cách sử dụng
- Mở trang upload ảnh vừa tạo trên Blogspot.
- Kéo/thả nhiều ảnh hoặc nhấn để chọn từ máy tính (JPG, PNG, GIF, dưới 5MB, tối đa 20 ảnh/lần).
- Đợi vài giây để ảnh upload lên Cloudinary.
-
Nhận danh sách link ảnh (bắt đầu bằng
https://res.cloudinary.com/) kèm hình xem trước. - Nhấn Copy để sao chép từng link hoặc Copy tất cả link để lấy toàn bộ.
Đặc điểm nổi bật
- Upload nhanh gọn: Kéo thả hoặc chọn file từ máy, vài giây có ngay link.
- Copy link 1 chạm: Mỗi ảnh có nút Copy riêng, hoặc Copy tất cả link một lần.
- Xem trước trực tiếp: Ảnh hiển thị preview ngay sau khi upload.
- Tự động tối ưu: Tích hợp sẵn f_auto,q_auto giúp ảnh nhẹ mà vẫn nét.
- Giới hạn thông minh: Chặn file > 5MB, chỉ cho phép JPG/PNG/GIF, tối đa 20 ảnh/lần.
- Miễn phí & ổn định: Dựa trên Cloudinary CDN, gói free thoải mái cho nhu cầu blog cá nhân.
- Tùy biến dễ dàng: Các màu sắc, giới hạn upload, thông báo... đều đã đánh dấu để bạn đổi theo gu.
DEMO TIỆN ÍCH TẠI ĐÂY
Theo truongdevs.com

0 Comments:
Post a Comment