سؤال

خلفية

أنا أكتب وأستخدم أداة إدارة محتوى بسيطة جدًا تعتمد على CGI (Perl) لاثنين من مواقع الويب المجانية.وهو يزود مسؤول موقع الويب بنماذج HTML للأحداث حيث يقوم بملء الحقول (التاريخ والمكان والعنوان والوصف والروابط وما إلى ذلك) وحفظها.في هذا النموذج، أسمح للمسؤول بتحميل صورة متعلقة بالحدث.في صفحة HTML التي تعرض النموذج، أعرض أيضًا معاينة للصورة التي تم تحميلها (علامة HTML img).

المشكلة

تحدث المشكلة عندما يريد المسؤول تغيير الصورة.كل ما عليه فعله هو الضغط على زر "تصفح" واختيار صورة جديدة والضغط على "موافق".وهذا يعمل بشكل جيد.

بمجرد تحميل الصورة، تتولى واجهة CGI الخلفية عملية التحميل وتعيد تحميل النموذج بشكل صحيح.

المشكلة هي أن الصورة المعروضة لا انتعش.لا تزال الصورة القديمة معروضة، على الرغم من أن قاعدة البيانات تحتوي على الصورة الصحيحة.لقد قمت بتضييق نطاق الأمر إلى حقيقة أن الصورة مخزنة مؤقتًا في متصفح الويب.إذا قام المسؤول بالضغط على زر RELOAD في Firefox/Explorer/Safari، فسيتم تحديث كل شيء بشكل جيد وتظهر الصورة الجديدة.

الحل الخاص بي - لا يعمل

أحاول التحكم في ذاكرة التخزين المؤقت عن طريق كتابة تعليمات HTTP انتهاء الصلاحية بتاريخ بعيد جدًا في الماضي.

Expires: Mon, 15 Sep 2003 1:00:00 GMT

تذكر أنني في الجانب الإداري ولا أهتم حقًا إذا استغرق تحميل الصفحات وقتًا أطول قليلاً لأنها تنتهي صلاحيتها دائمًا.

ولكن هذا لا يعمل أيضا.

ملحوظات

عند تحميل صورة، لا يتم الاحتفاظ باسم الملف الخاص بها في قاعدة البيانات.تمت إعادة تسميته باسم الصورة.jpg (ببساطة لتوضيح الأمور عند استخدامه).عند استبدال الصورة الموجودة بصورة جديدة، لا يتغير الاسم أيضًا.يتغير محتوى ملف الصورة فقط.

يتم توفير خادم الويب بواسطة خدمة الاستضافة/مزود خدمة الإنترنت.ويستخدم أباتشي.

سؤال

هل هناك طريقة لإجبار متصفح الويب على عدم تخزين الأشياء من هذه الصفحة، ولا حتى الصور؟

أنا أتعامل مع خيار "حفظ اسم الملف" في قاعدة البيانات.بهذه الطريقة، إذا تم تغيير الصورة، فسيتم أيضًا تغيير src الخاص بعلامة IMG.ومع ذلك، فإن هذا يتطلب الكثير من التغييرات في جميع أنحاء الموقع وأنا أفضل عدم القيام بذلك إذا كان لدي حل أفضل.أيضًا، لن يعمل هذا إذا كانت الصورة الجديدة التي تم تحميلها لها نفس الاسم (لنفترض أن الصورة تم تعديلها بالفوتوشوب قليلاً وتم إعادة تحميلها).

هل كانت مفيدة؟

المحلول

لدى أرمين روناشر الفكرة الصحيحة.المشكلة هي أن السلاسل العشوائية يمكن أن تتصادم.سأستخدم:

<img src="picture.jpg?1222259157.415" alt="">

حيث "1222259157.415" هو الوقت الحالي على الخادم.
توليد الوقت عن طريق جافا سكريبت مع performance.now() أو بواسطة بايثون مع time.time()

نصائح أخرى

إصلاح بسيط:إرفاق سلسلة استعلام عشوائية بالصورة:

<img src="foo.cgi?random=323527528432525.24234" alt="">

ما يقوله HTTP RFC:

Cache-Control: no-cache

لكن هذا لا يعمل بشكل جيد :)

أنا أستعمل وظيفة الوقت المعدلة لملف PHP, ، على سبيل المثال:

echo <img  src='Images/image.png?" . filemtime('Images/image.png') . "'  />";

إذا قمت بتغيير الصورة، فسيتم استخدام الصورة الجديدة بدلاً من الصورة المخزنة مؤقتًا، نظرًا لوجود طابع زمني معدل مختلف.

سأستخدم:

<img src="picture.jpg?20130910043254">

حيث "20130910043254" هو وقت تعديل الملف.

عند تحميل صورة، لا يتم الاحتفاظ باسم الملف الخاص بها في قاعدة البيانات.تمت إعادة تسميته كـ Image.jpg (لتوضيح الأمور عند استخدامه).عند استبدال الصورة الموجودة بصورة جديدة، لا يتغير الاسم أيضًا.يتغير محتوى ملف الصورة فقط.

أعتقد أن هناك نوعين من الحلول البسيطة:1) تلك التي تتبادر إلى ذهنك أولاً (الحلول المباشرة، لأنه من السهل التوصل إليها)، 2) تلك التي ينتهي بك الأمر إليها بعد التفكير في الأمور (لأنها سهلة الاستخدام).على ما يبدو، لن تستفيد دائمًا إذا اخترت التفكير في الأمور.لكن أعتقد أن الخيار الثاني تم الاستهانة به إلى حد ما.فقط فكر في السبب php تحظى بشعبية كبيرة ;)

يمكنك كتابة برنامج نصي وكيل لعرض الصور - وهذا يتطلب المزيد من العمل.شيء مثل هذا:

لغة البرمجة:

<img src="image.php?img=imageFile.jpg&some-random-number-262376" />

النصي:

// PHP
if( isset( $_GET['img'] ) && is_file( IMG_PATH . $_GET['img'] ) ) {

  // read contents
  $f = open( IMG_PATH . $_GET['img'] );
  $img = $f.read();
  $f.close();

  // no-cache headers - complete set
  // these copied from [php.net/header][1], tested myself - works
  header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Some time in the past
  header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); 
  header("Cache-Control: no-store, no-cache, must-revalidate"); 
  header("Cache-Control: post-check=0, pre-check=0", false); 
  header("Pragma: no-cache"); 

  // image related headers
  header('Accept-Ranges: bytes');
  header('Content-Length: '.strlen( $img )); // How many bytes we're going to send
  header('Content-Type: image/jpeg'); // or image/png etc

  // actual image
  echo $img;
  exit();
}

في الواقع، يجب أن تكون الرؤوس التي لا تحتوي على ذاكرة تخزين مؤقت أو الرقم العشوائي في الصورة src كافية، ولكن بما أننا نريد أن نكون مقاومين للرصاص..

أنا مبرمج جديد، ولكن إليك ما توصلت إليه، لإيقاف المتصفح من التخزين المؤقت والاحتفاظ بعروض كاميرا الويب الخاصة بي:

<meta Http-Equiv="Cache" content="no-cache">
<meta Http-Equiv="Pragma-Control" content="no-cache">
<meta Http-Equiv="Cache-directive" Content="no-cache">
<meta Http-Equiv="Pragma-directive" Content="no-cache">
<meta Http-Equiv="Cache-Control" Content="no-cache">
<meta Http-Equiv="Pragma" Content="no-cache">
<meta Http-Equiv="Expires" Content="0">
<meta Http-Equiv="Pragma-directive: no-cache">
<meta Http-Equiv="Cache-directive: no-cache">

لست متأكدًا مما يعمل على أي متصفح، ولكنه يعمل مع البعض:أي:يعمل عند تحديث صفحة الويب وعند إعادة زيارة موقع الويب (بدون تحديث).كروم:يعمل فقط عند تحديث صفحة الويب (حتى بعد إعادة الزيارة).رحلات السفاري والآيباد:لا يعمل، لا بد لي من مسح السجل وبيانات الويب.

أي أفكار حول SAFARI/ iPad؟

يستخدم فئة = "لا يوجد ذاكرة تخزين مؤقت"

نموذج HTML:

<div>
    <img class="NO-CACHE" src="images/img1.jpg" />
    <img class="NO-CACHE" src="images/imgLogo.jpg" />
</div>

مسج:

    $(document).ready(function ()
    {           
        $('.NO-CACHE').attr('src',function () { return $(this).attr('src') + "?a=" + Math.random() });
    });

جافا سكريبت:

var nods = document.getElementsByClassName('NO-CACHE');
for (var i = 0; i < nods.length; i++)
{
    nods[i].attributes['src'].value += "?a=" + Math.random();
}

نتيجة:سرك = "صور/img1.jpg" => src="images/img1.jpg?a=0.08749723793963926"

عند تحميل صورة، لا يتم الاحتفاظ باسم الملف الخاص بها في قاعدة البيانات.تمت إعادة تسميته كـ Image.jpg (لتوضيح الأمور عند استخدامه).

قم بتغيير هذا، وقد أصلحت مشكلتك.أستخدم الطوابع الزمنية، كما هو الحال مع الحلول المقترحة أعلاه: صورة-<الطابع الزمني>.jpg

من المفترض، مهما كانت المشاكل التي تتجنبها عن طريق الاحتفاظ بنفس اسم الملف للصورة، أنه يمكن التغلب عليها، لكنك لا تذكر ما هي.

لقد تحققت من جميع الإجابات الموجودة على الويب ويبدو أن أفضلها هو:(في الواقع ليس كذلك)

<img src="image.png?cache=none">

في البدايه.

ومع ذلك، إذا قمت بإضافة ذاكرة التخزين المؤقت = لا شيء المعلمة (وهي كلمة ثابتة "لا شيء")، فهي لا تؤثر على أي شيء، ولا يزال المتصفح يقوم بالتحميل من ذاكرة التخزين المؤقت.

الحل لهذه المشكلة كان:

<img src="image.png?nocache=<?php echo time(); ?>">

حيث تقوم بشكل أساسي بإضافة طابع زمني لنظام التشغيل Unix لجعل المعلمة ديناميكية وبدون ذاكرة تخزين مؤقت، لقد نجح الأمر.

لكن مشكلتي كانت مختلفة بعض الشيء:كنت أقوم بتحميل صورة مخطط php التي تم إنشاؤها بسرعة، وأتحكم في الصفحة باستخدام معلمات $_GET.أردت أن تتم قراءة الصورة من ذاكرة التخزين المؤقت عندما تظل معلمة GET لعنوان URL كما هي، ولا يتم تخزينها مؤقتًا عندما تتغير معلمات GET.

لحل هذه المشكلة، كنت بحاجة إلى تجزئة $_GET ولكن بما أنها مصفوفة، فهذا هو الحل:

$chart_hash = md5(implode('-', $_GET));
echo "<img src='/images/mychart.png?hash=$chart_hash'>";

يحرر:

على الرغم من أن الحل أعلاه يعمل بشكل جيد، إلا أنك في بعض الأحيان ترغب في عرض النسخة المخزنة مؤقتًا حتى يتم تغيير الملف.(مع الحل أعلاه ، فإنه يعطل ذاكرة التخزين المؤقت لتلك الصورة تمامًا) لذلك ، لتقديم الصورة المخزنة مؤقتًا من المتصفح حتى يتم تغيير ملف الصورة:

echo "<img src='/images/mychart.png?hash=" . filemtime('mychart.png') . "'>";

يحصل filemtime() على وقت تعديل الملف.

مشكلتك هي أنه على الرغم من Expires: الرأس، فإن متصفحك يعيد استخدام نسخته الموجودة في الذاكرة من الصورة قبل تحديثها، بدلاً من التحقق من ذاكرة التخزين المؤقت الخاصة به.

لقد واجهت موقفًا مشابهًا جدًا عند تحميل صور المنتج في الواجهة الخلفية للمسؤول لموقع يشبه المتجر، وفي حالتي قررت أن الخيار الأفضل هو استخدام جافا سكريبت لفرض تحديث الصورة، دون استخدام أي من تقنيات تعديل عنوان URL لأشخاص آخرين وقد ذكرت بالفعل هنا.بدلاً من ذلك، قمت بوضع عنوان URL للصورة في IFRAME مخفي، يسمى location.reload(true) في نافذة IFRAME، ثم استبدلت صورتي على الصفحة.وهذا يفرض تحديث الصورة، ليس فقط على الصفحة التي أتواجد فيها، ولكن أيضًا على أي صفحات لاحقة أزورها - دون أن يضطر العميل أو الخادم إلى تذكر أي سلسلة استعلام لعنوان URL أو معلمات معرف الجزء.

لقد نشرت بعض التعليمات البرمجية للقيام بذلك في إجابتي هنا.

أضف طابعًا زمنيًا <img src="picture.jpg?t=<?php echo time();?>">

سيعطي ملفك دائمًا رقمًا عشوائيًا في النهاية ويوقف تخزينه مؤقتًا

مع احتمال وجود وكلاء شفافين يتصرفون بشكل سيء بينك وبين العميل، فإن الطريقة الوحيدة لضمان عدم تخزين الصور مؤقتًا هي منحها عنوان uri فريدًا، مثل وضع علامة على طابع زمني كسلسلة استعلام أو كجزء من طريق.

إذا كان هذا الطابع الزمني يتوافق مع آخر وقت تحديث للصورة، فيمكنك تخزينها مؤقتًا عندما تحتاج إلى ذلك وعرض الصورة الجديدة في الوقت المناسب تمامًا.

أفترض أن السؤال الأصلي يتعلق بالصور المخزنة مع بعض المعلومات النصية.لذلك، إذا كان لديك حق الوصول إلى سياق النص عند إنشاء src=...url، فكر في تخزين/استخدام CRC32 من بايتات الصورة بدلاً من الطابع العشوائي أو الزمني الذي لا معنى له.وبعد ذلك، إذا تم عرض الصفحة التي تحتوي على الكثير من الصور، فسيتم إعادة تحميل الصور المحدثة فقط.في النهاية، إذا كان تخزين CRC مستحيلًا، فيمكن حسابه وإلحاقه بعنوان URL في وقت التشغيل.

من وجهة نظري، يعد تعطيل التخزين المؤقت للصور فكرة سيئة.على الاطلاق.

المشكلة الأساسية هنا هي كيفية إجبار المتصفح على تحديث الصورة، عندما يتم تحديثها من جانب الخادم.

مرة أخرى، من وجهة نظري الشخصية، الحل الأفضل هو تعطيل الوصول المباشر إلى الصور.بدلاً من ذلك، قم بالوصول إلى الصور عبر عامل التصفية/servlet/الأدوات/الخدمات الأخرى المشابهة من جانب الخادم.

في حالتي، فهي خدمة راحة تقوم بإرجاع الصورة وإرفاق علامة ETag ردًا على ذلك.تحتفظ الخدمة بتجزئة جميع الملفات، وإذا تم تغيير الملف، يتم تحديث التجزئة.إنه يعمل بشكل مثالي في جميع المتصفحات الحديثة.نعم، يستغرق تنفيذه وقتًا، لكنه يستحق ذلك.

الاستثناء الوحيد - هي المفضلة.لبعض الأسباب، فإنه لا يعمل.لم أتمكن من إجبار المتصفح على تحديث ذاكرة التخزين المؤقت الخاصة به من جانب الخادم.ETags، التحكم في ذاكرة التخزين المؤقت، انتهاء الصلاحية، رؤوس Pragma، لم يساعد أي شيء.

في هذه الحالة، يبدو أن إضافة بعض المعلمات العشوائية/الإصدار إلى عنوان url هو الحل الوحيد.

من الناحية المثالية، يجب عليك إضافة زر/ربط مفاتيح/قائمة إلى كل صفحة ويب مع خيار لمزامنة المحتوى.

للقيام بذلك، يمكنك تتبع الموارد التي قد تحتاج إلى المزامنة، وإما استخدام xhr لاستكشاف الصور باستخدام سلسلة استعلام ديناميكية، أو إنشاء صورة في وقت التشغيل باستخدام src باستخدام سلسلة استعلام ديناميكية.ثم استخدم آلية البث لإخطار جميع مكونات صفحات الويب التي تستخدم المورد لتحديثها لاستخدام المورد باستخدام QueryString ديناميكي تم إلحاقه بعنوان URL.

المثال الساذج يبدو كالتالي:

عادةً، يتم عرض الصورة وتخزينها مؤقتًا، ولكن إذا ضغط المستخدم على الزر، يتم إرسال طلب xhr إلى المورد مع سلسلة استعلام زمنية ملحقة به؛نظرًا لأنه من المفترض أن يكون الوقت مختلفًا في كل ضغطة، فإنه سيتأكد من أن المتصفح سيتجاوز ذاكرة التخزين المؤقت لأنه لا يمكنه معرفة ما إذا كان المورد قد تم إنشاؤه ديناميكيًا على جانب الخادم بناءً على الاستعلام، أو ما إذا كان ثابتًا المورد الذي يتجاهل الاستعلام.

والنتيجة هي أنه يمكنك تجنب تعرض جميع المستخدمين لإمطارك بطلبات الموارد طوال الوقت، ولكن في الوقت نفسه، اسمح بآلية للمستخدمين لتحديث مواردهم إذا اشتبهوا في أنها غير متزامنة.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="mobile-web-app-capable" content="yes" />        
    <title>Resource Synchronization Test</title>
    <script>
function sync() {
    var xhr = new XMLHttpRequest;
    xhr.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {            
            var images = document.getElementsByClassName("depends-on-resource");

            for (var i = 0; i < images.length; ++i) {
                var image = images[i];
                if (image.getAttribute('data-resource-name') == 'resource.bmp') {
                    image.src = 'resource.bmp?i=' + new Date().getTime();                
                }
            }
        }
    }
    xhr.open('GET', 'resource.bmp', true);
    xhr.send();
}
    </script>
  </head>
  <body>
    <img class="depends-on-resource" data-resource-name="resource.bmp" src="resource.bmp"></img>
    <button onclick="sync()">sync</button>
  </body>
</html>
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top