2011-11-07 33 views
21

Tôi tải một tập tin với mechanize và trong tiêu đề phản ứng có một chuỗi:Làm thế nào để có được tên tập tin từ Content-Disposition trong tiêu đề

Content-Disposition: attachment; filename=myfilename.txt 

Có một cách tiêu chuẩn nhanh chóng để có được giá trị tên tập tin? Những gì tôi có trong tâm trí bây giờ là:

filename = f[1]['Content-Disposition'].split('; ')[1].replace('filename=', '') 

Nhưng nó trông giống như một giải pháp nhanh chóng.

+0

Cũng giống như cảnh báo, tên tệp có thể được trích dẫn (như hầu hết các tiêu đề thư) và có chuỗi thoát. Vì vậy, hack chuỗi nhanh chóng có thể dẫn đến các vấn đề. –

Trả lời

24

đầu tiên nhận được giá trị của tiêu đề bởi sử dụng cơ giới hóa, sau đó phân tích cú pháp tiêu đề bằng cách sử dụng mô đun cgi được dựng sẵn.

Để chứng minh:

>>> import mechanize 
>>> browser = mechanize.Browser() 
>>> response = browser.open('http://example.com/your/url') 
>>> info = response.info() 
>>> header = info.getheader('Content-Disposition') 
>>> header 
'attachment; filename=myfilename.txt' 

Giá trị tiêu đề sau đó có thể được phân tích:

>>> import cgi    
>>> value, params = cgi.parse_header(header) 
>>> value 
'attachment' 
>>> params 
{'filename': 'myfilename.txt'} 

params là một dict đơn giản để params['filename'] là những gì bạn cần. Việc tên tệp có được bao gồm trong dấu ngoặc kép hay không quan trọng.

+0

Không phải điều này không hoạt động nếu tên tệp của bạn được mã hóa, trong trường hợp đó, tên lửa sẽ chứa' filename * 'thay vì' filename 'và bạn sẽ cần phải unquote và giải mã tên tệp thành chuỗi unicode. –

0

tôi sẽ cố gắng một cái gì đó như:

import re 
filename = re.findall("filename=(\S+)", f[1]['Content-Disposition']) 

này xử lý dấu ngoặc kép và URL thoát vào tên tập tin.

+1

Nhưng điều này trả về một danh sách, không phải là một chuỗi, vì vậy bạn có thể muốn 'filename [0]' hoặc một cái gì đó. Ngoài ra nó trả về các dấu ngoặc kép như là một phần của tên tệp. Vì vậy, không thực sự là một ví dụ làm việc. – unkulunkulu

5

Các cụm từ thông dụng này dựa trên ngữ pháp từ RFC 6266, nhưng được sửa đổi để chấp nhận tiêu đề mà không có loại bố trí, ví dụ: Nội dung-Bố trí: filename = example.html

tức là [loại bố trí ";" ] Bố trí-parm (";" bố trí-parm) */bố trí-loại

Nó sẽ xử lý các thông số tên tệp có và không có dấu ngoặc kép và bỏ cặp được trích dẫn từ giá trị trong dấu ngoặc kép, ví dụ: filename = "foo \" thanh "-> foo" thanh

Nó sẽ xử lý tên tập tin * thông số mở rộng và thích một tên tập tin * tham số mở rộng hơn một tham số tên tập tin không phụ thuộc vào thứ tự mà chúng xuất hiện trong tiêu đề

Nó dải thông tin tên thư mục, vd/etc/passwd -> passwd và mặc định là tên cơ sở từ đường dẫn URL khi không có tham số tên tệp (hoặc tiêu đề hoặc nếu giá trị tham số là chuỗi trống)

Biểu thức chính quy và qdtext dựa trên về ngữ pháp từ RFC 2616, biểu thức thông thường các mimeCharset và valueChars được dựa trên ngữ pháp từ RFC 5987, và các biểu hiện thường xuyên ngôn ngữ được dựa trên ngữ pháp từ RFC 5646

import re, urllib 
from os import path 
from urlparse import urlparse 

# content-disposition = "Content-Disposition" ":" 
#      disposition-type *(";" disposition-parm) 
# disposition-type = "inline" | "attachment" | disp-ext-type 
#      ; case-insensitive 
# disp-ext-type  = token 
# disposition-parm = filename-parm | disp-ext-parm 
# filename-parm  = "filename" "=" value 
#      | "filename*" "=" ext-value 
# disp-ext-parm  = token "=" value 
#      | ext-token "=" ext-value 
# ext-token   = <the characters in token, followed by "*"> 

token = '[-!#-\'*+.\dA-Z^-z|~]+' 
qdtext='[]-~\t !#-[]' 
mimeCharset='[-!#-&+\dA-Z^-z]+' 
language='(?:[A-Za-z]{2,3}(?:-[A-Za-z]{3}(?:-[A-Za-z]{3}){,2})?|[A-Za-z]{4,8})(?:-[A-Za-z]{4})?(?:-(?:[A-Za-z]{2}|\d{3}))(?:-(?:[\dA-Za-z]{5,8}|\d[\dA-Za-z]{3}))*(?:-[\dA-WY-Za-wy-z](?:-[\dA-Za-z]{2,8})+)*(?:-[Xx](?:-[\dA-Za-z]{1,8})+)?|[Xx](?:-[\dA-Za-z]{1,8})+|[Ee][Nn]-[Gg][Bb]-[Oo][Ee][Dd]|[Ii]-[Aa][Mm][Ii]|[Ii]-[Bb][Nn][Nn]|[Ii]-[Dd][Ee][Ff][Aa][Uu][Ll][Tt]|[Ii]-[Ee][Nn][Oo][Cc][Hh][Ii][Aa][Nn]|[Ii]-[Hh][Aa][Kk]|[Ii]-[Kk][Ll][Ii][Nn][Gg][Oo][Nn]|[Ii]-[Ll][Uu][Xx]|[Ii]-[Mm][Ii][Nn][Gg][Oo]|[Ii]-[Nn][Aa][Vv][Aa][Jj][Oo]|[Ii]-[Pp][Ww][Nn]|[Ii]-[Tt][Aa][Oo]|[Ii]-[Tt][Aa][Yy]|[Ii]-[Tt][Ss][Uu]|[Ss][Gg][Nn]-[Bb][Ee]-[Ff][Rr]|[Ss][Gg][Nn]-[Bb][Ee]-[Nn][Ll]|[Ss][Gg][Nn]-[Cc][Hh]-[Dd][Ee]' 
valueChars = '(?:%[\dA-F][\dA-F]|[-!#$&+.\dA-Z^-z|~])*' 
dispositionParm = '[Ff][Ii][Ll][Ee][Nn][Aa][Mm][Ee]\s*=\s*(?:({token})|"((?:{qdtext}|\\\\[\t !-~])*)")|[Ff][Ii][Ll][Ee][Nn][Aa][Mm][Ee]\*\s*=\s*({mimeCharset})\'(?:{language})?\'({valueChars})|{token}\s*=\s*(?:{token}|"(?:{qdtext}|\\\\[\t !-~])*")|{token}\*\s*=\s*{mimeCharset}\'(?:{language})?\'{valueChars}'.format(**locals()) 

try: 
    m = re.match('(?:{token}\s*;\s*)?(?:{dispositionParm})(?:\s*;\s*(?:{dispositionParm}))*|{token}'.format(**locals()), result.headers['Content-Disposition']) 

except KeyError: 
    name = path.basename(urllib.unquote(urlparse(url).path)) 

else: 
    if not m: 
    name = path.basename(urllib.unquote(urlparse(url).path)) 

    # Many user agent implementations predating this specification do not 
    # understand the "filename*" parameter. Therefore, when both "filename" 
    # and "filename*" are present in a single header field value, recipients 
    # SHOULD pick "filename*" and ignore "filename" 

    elif m.group(8) is not None: 
    name = urllib.unquote(m.group(8)).decode(m.group(7)) 

    elif m.group(4) is not None: 
    name = urllib.unquote(m.group(4)).decode(m.group(3)) 

    elif m.group(6) is not None: 
    name = re.sub('\\\\(.)', '\1', m.group(6)) 

    elif m.group(5) is not None: 
    name = m.group(5) 

    elif m.group(2) is not None: 
    name = re.sub('\\\\(.)', '\1', m.group(2)) 

    else: 
    name = m.group(1) 

    # Recipients MUST NOT be able to write into any location other than one to 
    # which they are specifically entitled 

    if name: 
    name = path.basename(name) 

    else: 
    name = path.basename(urllib.unquote(urlparse(url).path)) 
+0

Cách khác, các biểu thức chính quy có thể được đơn giản hóa bằng cách không xác thực thẻ ngôn ngữ, đặc biệt vì nó bị bỏ qua. Thẻ ngôn ngữ có thể chứa số dấu gạch ngang, số và chữ cái không bị chặn và tùy chọn. Vì vậy, chỉ chấp nhận [- \ dA-Za-z] * bố tríParm = '[Ff] [Ii] [Ll] [Ee] [Nn] [Aa] [Mm] [Ee] \ s * = \ s * (?: ({token}) | "((?: {qdtext} | \\\ [\ t! - ~]) *) ") | [Ff] [Ii] [Ll] [Ee] [Nn] [Aa] [Mm] [Ee] \ * \ s * = \ s * ({mimeCharset}) \ '[- \ dA-Za-z] * \' ({valueChars}) | {token} \ s * = \ s * (?: {token} | "(? : {qdtext} | \\\\ [\ t! - ~]) * ") | {token} \ * \ s * = \ s * {mimeCharset} \ '[- \ dA-Za-z] * \' Định dạng {valueChars} '(** locals()) – user916968

Các vấn đề liên quan