browser - شرح - http tutorial




كيف يتم ترميز معلمة اسم الملف لرأس Content-Disposition في HTTP؟ (12)

تُصدر تطبيقات الويب التي تريد فرض مورد ليتم تنزيله بدلاً من تقديمه مباشرة في مستعرض ويب رأس Content-Disposition في استجابة HTTP للنموذج:

Content-Disposition: attachment; filename= FILENAME

يمكن استخدام معلمة filename لاقتراح اسم للملف الذي يتم تنزيل المورد من خلاله. RFC 2183 (المحتوى - الترتيب) ، ومع ذلك ، ينص في المقطع 2.3 (معلمة اسم الملف) أن اسم الملف يمكن فقط استخدام أحرف US-ASCII:

القواعد الحالية [RFC 2045] تقيّد قيم المعلمات (وبالتالي أسماء ملفات ترتيب المحتوى) إلى US-ASCII. نحن ندرك الرغبة الكبيرة في السماح بمجموعات الحروف التعسفية في أسماء الملفات ، ولكنه يتجاوز نطاق هذه الوثيقة لتحديد الآليات الضرورية.

هناك أدلة تجريبية ، على الرغم من ذلك ، يبدو أن معظم متصفحات الويب الشائعة اليوم تسمح بأحرف غير US-ASCII حتى الآن (لعدم وجود معيار) لا تتفق على نظام الترميز ومواصفات مجموعة الأحرف لاسم الملف. السؤال إذن هو ، ما هي المخططات والتشفيرات المختلفة التي تستخدمها المتصفحات الشائعة إذا كان اسم الملف "naïvefile" (بدون علامتي الاقتباس وحيث يكون الحرف الثالث هو U + 00EF) يلزم تشفيره في رأسية ترتيب المحتوى؟

لغرض هذا السؤال ، أصبحت المتصفحات الشائعة :

  • ثعلب النار
  • متصفح الانترنت
  • رحلات السفاري
  • جوجل كروم
  • دار الأوبرا

كلاسيك ASP الحل

تدعم معظم المتصفحات الحديثة تمرير Filename كـ UTF-8 الآن ولكن كما كان الحال مع حل "تحميل الملف" الذي FreeASPUpload.Net والذي كان يعتمد على FreeASPUpload.Net (لم يعد الموقع موجودًا ، أو نقاط الارتباط إلى archive.org ) فلن يعمل اعتمد تحليل البيانات الثنائية على قراءة السلاسل المشفرة ASCII أحادية البايت ، والتي عملت بشكل جيد عند تمرير البيانات المشفرة UTF-8 حتى تصل إلى الأحرف التي لا تدعمها ASCII.

ومع ذلك ، تمكنت من العثور على حل للحصول على التعليمات البرمجية لقراءة وتحليل ثنائي كـ UTF-8.

Public Function BytesToString(bytes)    'UTF-8..
  Dim bslen
  Dim i, k , N 
  Dim b , count 
  Dim str

  bslen = LenB(bytes)
  str=""

  i = 0
  Do While i < bslen
    b = AscB(MidB(bytes,i+1,1))

    If (b And &HFC) = &HFC Then
      count = 6
      N = b And &H1
    ElseIf (b And &HF8) = &HF8 Then
      count = 5
      N = b And &H3
    ElseIf (b And &HF0) = &HF0 Then
      count = 4
      N = b And &H7
    ElseIf (b And &HE0) = &HE0 Then
      count = 3
      N = b And &HF
    ElseIf (b And &HC0) = &HC0 Then
      count = 2
      N = b And &H1F
    Else
      count = 1
      str = str & Chr(b)
    End If

    If i + count - 1 > bslen Then
      str = str&"?"
      Exit Do
    End If

    If count>1 then
      For k = 1 To count - 1
        b = AscB(MidB(bytes,i+k+1,1))
        N = N * &H40 + (b And &H3F)
      Next
      str = str & ChrW(N)
    End If
    i = i + count
  Loop

  BytesToString = str
End Function

يذهب الائتمان إلى "إيداع ملف ASP النقي" عن طريق تنفيذ الدالة BytesToString() من include_aspuploader.asp في التعليمة البرمجية الخاصة بي كنت قادراً على الحصول UTF-8 أسماء ملفات UTF-8 تعمل.

روابط مفيدة

  • Multipart / form-data و UTF-8 في تطبيق ASP كلاسيكي

  • Unicode ، UTF ، ASCII ، اختلافات تنسيق ANSI


أستخدم مقتطفات الشفرة التالية للتشفير (بافتراض أن fileName يحتوي على اسم الملف وامتداده ، أي: test.txt):

PHP:

if ( strpos ( $_SERVER [ 'HTTP_USER_AGENT' ], "MSIE" ) > 0 )
{
     header ( 'Content-Disposition: attachment; filename="' . rawurlencode ( $fileName ) . '"' );
}
else
{
     header( 'Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode ( $fileName ) );
}

جافا:

fileName = request.getHeader ( "user-agent" ).contains ( "MSIE" ) ? URLEncoder.encode ( fileName, "utf-8") : MimeUtility.encodeWord ( fileName );
response.setHeader ( "Content-disposition", "attachment; filename=\"" + fileName + "\"");

إذا كنت تستخدم backode nodejs ، فيمكنك استخدام الكود التالي الذي وجدته here

var fileName = 'my file(2).txt';
var header = "Content-Disposition: attachment; filename*=UTF-8''" 
             + encodeRFC5987ValueChars(fileName);

function encodeRFC5987ValueChars (str) {
    return encodeURIComponent(str).
        // Note that although RFC3986 reserves "!", RFC5987 does not,
        // so we do not need to escape it
        replace(/['()]/g, escape). // i.e., %27 %28 %29
        replace(/\*/g, '%2A').
            // The following are not required for percent-encoding per RFC5987, 
            // so we can allow for a little better readability over the wire: |`^
            replace(/%(?:7C|60|5E)/g, unescape);
}

اختبرت الشفرة التالية في جميع المتصفحات الرئيسية ، بما في ذلك المستكشفون الأقدم (عبر وضع التوافق) ، وهي تعمل بشكل جيد في كل مكان:

$filename = $_GET['file']; //this string from $_GET is already decoded
if (strstr($_SERVER['HTTP_USER_AGENT'],"MSIE"))
  $filename = rawurlencode($filename);
header('Content-Disposition: attachment; filename="'.$filename.'"');

انتهى بي المطاف مع التعليمات البرمجية التالية في البرنامج النصي "download.php" الخاص بي (استناداً إلى هذا blogpost وحالات الاختبار هذه ).

$il1_filename = utf8_decode($filename);
$to_underscore = "\"\\#*;:|<>/?";
$safe_filename = strtr($il1_filename, $to_underscore, str_repeat("_", strlen($to_underscore)));

header("Content-Disposition: attachment; filename=\"$safe_filename\""
.( $safe_filename === $filename ? "" : "; filename*=UTF-8''".rawurlencode($filename) ));

يستخدم هذا الطريقة القياسية filename = "..." طالما لا يوجد سوى أحرف iso-latin1 و "safe" المستخدمة ؛ إذا لم يكن كذلك ، فإنه يضيف اسم الملف * = UTF-8 '' url-encoded way. وفقًا لحالة الاختبار المحددة هذه ، يجب أن تعمل من MSIE9 ، وعلى FF ، Chrome ، Safari حديثًا ؛ في إصدار MSIE السفلي ، يجب أن يقدم اسم الملف الذي يحتوي على إصدار ISO8859-1 من اسم الملف ، مع وجود أحرف سفلية على أحرف ليست في هذا الترميز.

ملاحظة أخيرة: الحد الأقصى حجم لكل حقل رأس 8190 بايت على اباتشي. يمكن أن يصل UTF-8 إلى أربعة بايت لكل حرف؛ بعد rawurlencode ، يكون x3 = 12 بايت لكل حرف واحد. غير فعالة إلى حد ما ، ولكن يجب أن يكون من الممكن نظريًا الحصول على أكثر من 600 "ابتسامة"٪ F0٪ 9F٪ 98٪ 81 في اسم الملف.



في PHP هذا بالنسبة لي (بافتراض أن اسم الملف هو UTF8 مشفر):

header('Content-Disposition: attachment;'
    . 'filename="' . addslashes(utf8_decode($filename)) . '";'
    . 'filename*=utf-8\'\'' . rawurlencode($filename));

تم اختباره ضد IE8-11 و Firefox و Chrome.
إذا كان المستعرض يستطيع تفسير filename * = utf-8 ، فسيستخدم إصدار UTF8 من اسم الملف ، وإلا فسيستخدم اسم الملف المشفر. إذا كان اسم الملف الخاص بك يحتوي على أحرف لا يمكن تمثيلها في ISO-8859-1 ، فربما ترغب في استخدام iconv بدلاً من ذلك.


في asp.net mvc2 يمكنني استخدام شيء من هذا القبيل:

return File(
    tempFile
    , "application/octet-stream"
    , HttpUtility.UrlPathEncode(fileName)
    );

أعتقد إذا كنت لا تستخدم mvc (2) يمكنك فقط ترميز اسم الملف باستخدام

HttpUtility.UrlPathEncode(fileName)

لقد اكتشفت حلًا يعمل مع جميع المتصفحات (أي جميع المتصفحات التي قمت بتثبيتها - IE8 و FF16 و Opera 12 و Chrome 22).

يتم وصف حل بلدي في موضوع آخر: جافا servlet تنزيل اسم الملف أحرف خاصة

ويستند حل بلدي على حقيقة ، وكيف يحاول المتصفحات قراءة القيمة من معلمة filename . إذا لم تكن هناك مجموعة أحرف محددة في معلمة filename (على سبيل المثال filename*=utf-8''test.xml ) ، تتوقع المتصفحات ترميز القيمة في الترميز الأصلي للمتصفح.

وتتوقع المتصفحات المختلفة ترميزًا أصليًا مختلفًا. عادة ما يكون الترميز الأصلي للمستعرض هو utf-8 (FireFox ، Opera ، Chrome). لكن الترميز الأصلي لـ IE هو Win-1250. (لا أعرف أي شيء عن المتصفحات الأخرى.)

ومن ثم ، إذا وضعنا قيمة في filename parametr ، التي تم ترميزها بواسطة utf-8 / win-1250 وفقًا لمتصفح المستخدم ، يجب أن تعمل. على الأقل بالنسبة لي كان يعمل.

باختصار ، إذا كان لدينا ملف اسمه omáčka.xml ،
بالنسبة إلى كل من Firefox و Opera و Chrome I ، استجاب هذا العنوان (المشفر في utf-8):

Content-Disposition: attachment; filename="omáčka.xml"

و IE أنا الرد على هذا الرأس (المشفرة في الفوز -1250):

Content-Disposition: attachment; filename="omáèka.jpg"

مثال Java موجود في مشاركتي المذكورة أعلاه.


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

يشير RFC 2183 إلى أنه يجب ترميز مثل هذه الرؤوس وفقًا لـ RFC 2184 ، والتي تم استبدالها بواسطة RFC 2231 ، والتي تمت تغطيتها بواسطة مسودة RFC أعلاه.


يصف RFC 6266 " استخدام حقل رأس Content-Disposition Header في بروتوكول نقل النص التشعبي (HTTP) ". نقلا عن ذلك:

6. اعتبارات التدويل

تسمح معلمة " filename* " ( القسم 4.3 ) ، باستخدام التشفير المحدد في [ RFC5987 ] ، للخادم بإرسال أحرف خارج مجموعة الحروف ISO-8859-1 ، وكذلك تحديد اللغة المستخدمة اختياريًا.

وفي قسم الأمثلة الخاصة بهم:

هذا المثال هو نفس المثال أعلاه ، ولكن إضافة معلمة "filename" للتوافق مع وكلاء المستخدم لا تنفذ RFC 5987 :

Content-Disposition: attachment;
                     filename="EURO rates";
                     filename*=utf-8''%e2%82%ac%20rates

ملاحظة: تتجاهل وكلاء المستخدم هؤلاء الذين لا يدعمون ترميز RFC 5987 " filename* " عندما يحدث بعد " filename ".

في الملحق D هناك أيضًا قائمة طويلة من الاقتراحات لزيادة قابلية التشغيل المتداخل. يشير أيضًا إلى موقع يقارن عمليات التنفيذ . تتضمن اختبارات تمرير الكل الحالية المناسبة لأسماء الملفات الشائعة ما يلي:

  • attwithisofnplain : اسم ملف ISO-8859-1 عادي مع علامات اقتباس مزدوجة وبدون تشفير. هذا يتطلب اسم ملف هو كل ISO-8859-1 ولا يحتوي على علامات النسبة المئوية ، على الأقل ليس أمام الأرقام السداسية.
  • attfnboth : attfnboth بالترتيب الموصوف أعلاه. يجب أن تعمل مع معظم أسماء الملفات في معظم المتصفحات ، على الرغم من استخدام IE8 لمعلمة " filename ".

يشير RFC 5987 بدوره إلى RFC 2231 ، الذي يصف التنسيق الفعلي. 2231 هو في المقام الأول للبريد ، ويخبرنا 5987 ما هي الأجزاء التي يمكن استخدامها لرؤوس HTTP أيضًا. لا تخلط بين هذا ورؤوس MIME المستخدمة داخل نص HTTP multipart/form-data ، والذي يحكمه RFC 2388 ( القسم 4.4 على وجه الخصوص) ومسودة HTML 5 .


  • لا توجد طريقة قابلة للتشغيل المتبادل لترميز أسماء غير ASCII في Content-Disposition . توافق المتصفح هو الفوضى .

  • greenbytes.de/tech/webdav/rfc5987.html لاستخدام UTF-8 في Content-Disposition غريب جدًا: filename*=UTF-8''foo%c3%a4 (نعم ، هذه علامة نجمية ، ولا توجد علامات اقتباس باستثناء علامة اقتباس مفردة فارغة في المنتصف)

  • هذا الرأس ليس نوعًا ما قياسيًا ( تعترف مقاييس HTTP / 1.1 بوجودها ، ولكنها لا تتطلب من العملاء دعمها).

هناك بديل بسيط وقوي جدًا: استخدم عنوان URL يحتوي على اسم الملف الذي تريده .

عندما يكون الاسم بعد الخط المائل الأخير هو الذي تريده ، فأنت لا تحتاج إلى أية رؤوس إضافية!

هذه الخدعة تعمل:

/real_script.php/fake_filename.doc

وإذا كان خادمك يدعم إعادة كتابة عناوين URL (مثل mod_rewrite في Apache) ، فيمكنك حينئذٍ إخفاء جزء البرنامج النصي بالكامل.

يجب أن تكون الأحرف في عناوين URL بتنسيق UTF-8 ، urlencoded byte by byte:

/mot%C3%B6rhead   # motörhead




specifications