You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中如何隐藏仅由JavaScript触发显示的图片URL?

问题

我开发了一个Django应用,需求是用户点击指定id为PDDO的图片元素30次后,才将原图替换为另一张图片。当前实现通过img标签的data-over属性存储目标图片URL,但该URL可通过查看页面源码轻易获取。我尝试过如下JS方案但无效:

$("a[data-label='document.querySelector("#PDDO").dataset.over']").hide()

附当前核心实现代码:

var count = 0;

document.addEventListener('DOMContentLoaded', function(){
    document.querySelector("#PDDO").onclick = function(){
        if(count > 30){
        document.querySelector("#PDDO").src = document.querySelector("#PDDO").dataset.over
        }else{
            count++;
        }
}})
<img src="{% static 'database/images/original_image.png' %}" width="200", height="200", class="revolve" id="PDDO" data-over="{% static 'database/images/new_image.png' %}">
解决方案

方法1:后端存储计数并返回URL(最安全)

把点击计数逻辑移到后端,用Django的session存储用户点击次数,只有达标后才返回目标图片URL:

  1. 前端每次点击时发送AJAX请求到后端,更新计数
  2. 后端验证计数达标后,返回目标图片的静态URL
  3. 前端拿到URL后替换图片src

Django视图代码:

from django.http import JsonResponse
from django.templatetags.static import static

def update_click_count(request):
    if request.method != 'POST':
        return JsonResponse({'error': '无效请求'}, status=400)
    
    count = request.session.get('pddo_click_count', 0)
    count += 1
    request.session['pddo_click_count'] = count
    
    response_data = {'count': count}
    if count >= 30:
        response_data['target_url'] = static('database/images/new_image.png')
    
    return JsonResponse(response_data)

前端JS代码:

document.addEventListener('DOMContentLoaded', function(){
    const img = document.querySelector("#PDDO");
    img.onclick = async function(){
        const response = await fetch('/update-click-count/', {
            method: 'POST',
            headers: {
                'X-CSRFToken': getCookie('csrftoken'),
                'Content-Type': 'application/json'
            }
        });
        const data = await response.json();
        if (data.target_url) {
            img.src = data.target_url;
        }
    }
});

// 获取CSRF Token的辅助函数
function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

前端HTML(移除data-over属性):

<img src="{% static 'database/images/original_image.png' %}" width="200" height="200" class="revolve" id="PDDO">

方法2:前端加密存储URL(防普通用户)

如果不想依赖后端逻辑,可对目标URL进行简单加密后存在前端,计数达标再解密:

  1. 在Django模板中对目标URL做Base64加密
  2. 前端存储加密字符串,达标后解密替换src

自定义Django过滤器(在app的templatetags目录下创建base64.py):

from django import template
import base64

register = template.Library()

@register.filter
def base64_encode(value):
    return base64.b64encode(value.encode('utf-8')).decode('utf-8')

前端HTML代码:

{% load static %}
{% load base64 %}
<img src="{% static 'database/images/original_image.png' %}" width="200" height="200" class="revolve" id="PDDO" data-over-encrypted="{% static 'database/images/new_image.png'|base64_encode %}">

前端JS代码:

var count = 0;

document.addEventListener('DOMContentLoaded', function(){
    const img = document.querySelector("#PDDO");
    const encryptedUrl = img.dataset.overEncrypted;
    img.onclick = function(){
        count++;
        if(count >= 30){
            const decryptedUrl = atob(encryptedUrl);
            img.src = decryptedUrl;
        }
    }
});

注意:此方法仅增加获取门槛,技术人员仍可通过JS代码解密,适合低安全要求场景。

方法3:后端直接返回图片内容(完全隐藏URL)

计数达标后,后端直接返回图片二进制内容,而非URL,彻底隐藏真实路径:

  1. 前端点击达标后,请求后端图片接口
  2. 后端验证计数后,返回图片文件
  3. 前端将img的src指向该接口

Django视图代码:

from django.http import HttpResponse
from django.templatetags.static import static
import os

def get_target_image(request):
    count = request.session.get('pddo_click_count', 0)
    # 根据计数返回对应图片
    if count >= 30:
        img_path = static('database/images/new_image.png').lstrip('/')
    else:
        img_path = static('database/images/original_image.png').lstrip('/')
    
    with open(img_path, 'rb') as f:
        return HttpResponse(f.read(), content_type='image/png')

前端JS代码(复用方法1的update_click_count接口):

document.addEventListener('DOMContentLoaded', function(){
    const img = document.querySelector("#PDDO");
    img.onclick = async function(){
        const response = await fetch('/update-click-count/', {
            method: 'POST',
            headers: {
                'X-CSRFToken': getCookie('csrftoken'),
                'Content-Type': 'application/json'
            }
        });
        const data = await response.json();
        if (data.count >= 30) {
            img.src = '/get-target-image/';
        }
    }
});

// 复用getCookie函数
function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

内容的提问来源于stack exchange,提问作者Rivered

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 06:10:49