Set Unicode filename in Flask response header












3















I am trying to set the Content-Disposition header to send a file to the client. The file name is Unicode. When I try to set the header, it fails with a UnicodeEncodeError. I tried various combinations of encode and decode but couldn't get it to work. How can I send a file with a Unicode filename?



destination_file = 'python_report.html'
response.headers['Content-Disposition'] = 'attachment; filename=' + destination_file




  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/http/server.py", line 495, in send_header
("%s: %srn" % (keyword, value)).encode('latin-1', 'strict'))
UnicodeEncodeError: 'latin-1' codec can't encode characters in position 41-42: ordinal not in range(256)









share|improve this question





























    3















    I am trying to set the Content-Disposition header to send a file to the client. The file name is Unicode. When I try to set the header, it fails with a UnicodeEncodeError. I tried various combinations of encode and decode but couldn't get it to work. How can I send a file with a Unicode filename?



    destination_file = 'python_report.html'
    response.headers['Content-Disposition'] = 'attachment; filename=' + destination_file




      File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/http/server.py", line 495, in send_header
    ("%s: %srn" % (keyword, value)).encode('latin-1', 'strict'))
    UnicodeEncodeError: 'latin-1' codec can't encode characters in position 41-42: ordinal not in range(256)









    share|improve this question



























      3












      3








      3


      4






      I am trying to set the Content-Disposition header to send a file to the client. The file name is Unicode. When I try to set the header, it fails with a UnicodeEncodeError. I tried various combinations of encode and decode but couldn't get it to work. How can I send a file with a Unicode filename?



      destination_file = 'python_report.html'
      response.headers['Content-Disposition'] = 'attachment; filename=' + destination_file




        File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/http/server.py", line 495, in send_header
      ("%s: %srn" % (keyword, value)).encode('latin-1', 'strict'))
      UnicodeEncodeError: 'latin-1' codec can't encode characters in position 41-42: ordinal not in range(256)









      share|improve this question
















      I am trying to set the Content-Disposition header to send a file to the client. The file name is Unicode. When I try to set the header, it fails with a UnicodeEncodeError. I tried various combinations of encode and decode but couldn't get it to work. How can I send a file with a Unicode filename?



      destination_file = 'python_report.html'
      response.headers['Content-Disposition'] = 'attachment; filename=' + destination_file




        File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/http/server.py", line 495, in send_header
      ("%s: %srn" % (keyword, value)).encode('latin-1', 'strict'))
      UnicodeEncodeError: 'latin-1' codec can't encode characters in position 41-42: ordinal not in range(256)






      python python-3.x flask http-headers






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Apr 12 '17 at 15:47









      davidism

      63.9k12167185




      63.9k12167185










      asked Apr 12 '17 at 9:30









      n33rman33rma

      2,248926




      2,248926
























          1 Answer
          1






          active

          oldest

          votes


















          7














          RFC 2231 section 4 describes how to specify an encoding to use instead of Latin-1 for a header value. Use the header option filename*=UTF-8''..., where ... is the url-encoded name. You can also include the filename option to provide a Latin-1 fallback.



          Until relatively recently, browsers did not consistently support this. This page has some metrics on browser support. Notably, IE8 will ignore the UTF-8 option, and will fail completely if the UTF-8 option comes before the Latin-1 option.



          Flask 1.0 supports calling send_file with Unicode filenames. If you use Flask 1.0, you can use send_file with as_attachment=True and a Unicode filename.



          from flask import send_file

          @app.route('/send-python-report')
          def send_python_report():
          return send_file('python_report.html', as_attachment=True)


          Until then, you can construct the header manually using the same process Flask will use.



          import unicodedata

          from flask import send_file
          from werkzeug.urls import url_quote

          @app.route('/send-python-report')
          def send_python_report():
          filename = 'python_report.html'
          rv = send_file(filename)

          try:
          filename = filename.encode('latin-1')
          except UnicodeEncodeError:
          filenames = {
          'filename': unicodedata.normalize('NFKD', filename).encode('latin-1', 'ignore'),
          'filename*': "UTF-8''{}".format(url_quote(filename)),
          }
          else:
          filenames = {'filename': filename}

          rv.headers.set('Content-Disposition', 'attachment', **filenames)
          return rv




          For security you should use send_from_directory instead if the filename is provided by user input. The process is the same as above, substituting the function.





          WSGI doesn't ensure the order of header options, so if you want to support IE8 you must construct the header value completely manually using dump_options_header with an OrderedDict. Otherwise, filename* may appear before filename, which as noted above does not work in IE8.



          from collections import OrderedDict
          import unicodedata

          from flask import send_file
          from werkzeug.http import dump_options_header
          from werkzeug.urls import url_quote

          @app.route('/send-python-report')
          def send_python_report():
          filename = 'python_report.html'
          rv = send_file(filename)
          filenames = OrderedDict()

          try:
          filename = filename.encode('latin-1')
          except UnicodeEncodeError:
          filenames['filename'] = unicodedata.normalize('NFKD', filename).encode('latin-1', 'ignore')
          filenames['filename*']: "UTF-8''{}".format(url_quote(filename))
          else:
          filenames['filename'] = filename

          rv.headers.set('Content-Disposition', dump_options_header('attachment', filenames))
          return rv





          share|improve this answer

























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f43365640%2fset-unicode-filename-in-flask-response-header%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            7














            RFC 2231 section 4 describes how to specify an encoding to use instead of Latin-1 for a header value. Use the header option filename*=UTF-8''..., where ... is the url-encoded name. You can also include the filename option to provide a Latin-1 fallback.



            Until relatively recently, browsers did not consistently support this. This page has some metrics on browser support. Notably, IE8 will ignore the UTF-8 option, and will fail completely if the UTF-8 option comes before the Latin-1 option.



            Flask 1.0 supports calling send_file with Unicode filenames. If you use Flask 1.0, you can use send_file with as_attachment=True and a Unicode filename.



            from flask import send_file

            @app.route('/send-python-report')
            def send_python_report():
            return send_file('python_report.html', as_attachment=True)


            Until then, you can construct the header manually using the same process Flask will use.



            import unicodedata

            from flask import send_file
            from werkzeug.urls import url_quote

            @app.route('/send-python-report')
            def send_python_report():
            filename = 'python_report.html'
            rv = send_file(filename)

            try:
            filename = filename.encode('latin-1')
            except UnicodeEncodeError:
            filenames = {
            'filename': unicodedata.normalize('NFKD', filename).encode('latin-1', 'ignore'),
            'filename*': "UTF-8''{}".format(url_quote(filename)),
            }
            else:
            filenames = {'filename': filename}

            rv.headers.set('Content-Disposition', 'attachment', **filenames)
            return rv




            For security you should use send_from_directory instead if the filename is provided by user input. The process is the same as above, substituting the function.





            WSGI doesn't ensure the order of header options, so if you want to support IE8 you must construct the header value completely manually using dump_options_header with an OrderedDict. Otherwise, filename* may appear before filename, which as noted above does not work in IE8.



            from collections import OrderedDict
            import unicodedata

            from flask import send_file
            from werkzeug.http import dump_options_header
            from werkzeug.urls import url_quote

            @app.route('/send-python-report')
            def send_python_report():
            filename = 'python_report.html'
            rv = send_file(filename)
            filenames = OrderedDict()

            try:
            filename = filename.encode('latin-1')
            except UnicodeEncodeError:
            filenames['filename'] = unicodedata.normalize('NFKD', filename).encode('latin-1', 'ignore')
            filenames['filename*']: "UTF-8''{}".format(url_quote(filename))
            else:
            filenames['filename'] = filename

            rv.headers.set('Content-Disposition', dump_options_header('attachment', filenames))
            return rv





            share|improve this answer






























              7














              RFC 2231 section 4 describes how to specify an encoding to use instead of Latin-1 for a header value. Use the header option filename*=UTF-8''..., where ... is the url-encoded name. You can also include the filename option to provide a Latin-1 fallback.



              Until relatively recently, browsers did not consistently support this. This page has some metrics on browser support. Notably, IE8 will ignore the UTF-8 option, and will fail completely if the UTF-8 option comes before the Latin-1 option.



              Flask 1.0 supports calling send_file with Unicode filenames. If you use Flask 1.0, you can use send_file with as_attachment=True and a Unicode filename.



              from flask import send_file

              @app.route('/send-python-report')
              def send_python_report():
              return send_file('python_report.html', as_attachment=True)


              Until then, you can construct the header manually using the same process Flask will use.



              import unicodedata

              from flask import send_file
              from werkzeug.urls import url_quote

              @app.route('/send-python-report')
              def send_python_report():
              filename = 'python_report.html'
              rv = send_file(filename)

              try:
              filename = filename.encode('latin-1')
              except UnicodeEncodeError:
              filenames = {
              'filename': unicodedata.normalize('NFKD', filename).encode('latin-1', 'ignore'),
              'filename*': "UTF-8''{}".format(url_quote(filename)),
              }
              else:
              filenames = {'filename': filename}

              rv.headers.set('Content-Disposition', 'attachment', **filenames)
              return rv




              For security you should use send_from_directory instead if the filename is provided by user input. The process is the same as above, substituting the function.





              WSGI doesn't ensure the order of header options, so if you want to support IE8 you must construct the header value completely manually using dump_options_header with an OrderedDict. Otherwise, filename* may appear before filename, which as noted above does not work in IE8.



              from collections import OrderedDict
              import unicodedata

              from flask import send_file
              from werkzeug.http import dump_options_header
              from werkzeug.urls import url_quote

              @app.route('/send-python-report')
              def send_python_report():
              filename = 'python_report.html'
              rv = send_file(filename)
              filenames = OrderedDict()

              try:
              filename = filename.encode('latin-1')
              except UnicodeEncodeError:
              filenames['filename'] = unicodedata.normalize('NFKD', filename).encode('latin-1', 'ignore')
              filenames['filename*']: "UTF-8''{}".format(url_quote(filename))
              else:
              filenames['filename'] = filename

              rv.headers.set('Content-Disposition', dump_options_header('attachment', filenames))
              return rv





              share|improve this answer




























                7












                7








                7







                RFC 2231 section 4 describes how to specify an encoding to use instead of Latin-1 for a header value. Use the header option filename*=UTF-8''..., where ... is the url-encoded name. You can also include the filename option to provide a Latin-1 fallback.



                Until relatively recently, browsers did not consistently support this. This page has some metrics on browser support. Notably, IE8 will ignore the UTF-8 option, and will fail completely if the UTF-8 option comes before the Latin-1 option.



                Flask 1.0 supports calling send_file with Unicode filenames. If you use Flask 1.0, you can use send_file with as_attachment=True and a Unicode filename.



                from flask import send_file

                @app.route('/send-python-report')
                def send_python_report():
                return send_file('python_report.html', as_attachment=True)


                Until then, you can construct the header manually using the same process Flask will use.



                import unicodedata

                from flask import send_file
                from werkzeug.urls import url_quote

                @app.route('/send-python-report')
                def send_python_report():
                filename = 'python_report.html'
                rv = send_file(filename)

                try:
                filename = filename.encode('latin-1')
                except UnicodeEncodeError:
                filenames = {
                'filename': unicodedata.normalize('NFKD', filename).encode('latin-1', 'ignore'),
                'filename*': "UTF-8''{}".format(url_quote(filename)),
                }
                else:
                filenames = {'filename': filename}

                rv.headers.set('Content-Disposition', 'attachment', **filenames)
                return rv




                For security you should use send_from_directory instead if the filename is provided by user input. The process is the same as above, substituting the function.





                WSGI doesn't ensure the order of header options, so if you want to support IE8 you must construct the header value completely manually using dump_options_header with an OrderedDict. Otherwise, filename* may appear before filename, which as noted above does not work in IE8.



                from collections import OrderedDict
                import unicodedata

                from flask import send_file
                from werkzeug.http import dump_options_header
                from werkzeug.urls import url_quote

                @app.route('/send-python-report')
                def send_python_report():
                filename = 'python_report.html'
                rv = send_file(filename)
                filenames = OrderedDict()

                try:
                filename = filename.encode('latin-1')
                except UnicodeEncodeError:
                filenames['filename'] = unicodedata.normalize('NFKD', filename).encode('latin-1', 'ignore')
                filenames['filename*']: "UTF-8''{}".format(url_quote(filename))
                else:
                filenames['filename'] = filename

                rv.headers.set('Content-Disposition', dump_options_header('attachment', filenames))
                return rv





                share|improve this answer















                RFC 2231 section 4 describes how to specify an encoding to use instead of Latin-1 for a header value. Use the header option filename*=UTF-8''..., where ... is the url-encoded name. You can also include the filename option to provide a Latin-1 fallback.



                Until relatively recently, browsers did not consistently support this. This page has some metrics on browser support. Notably, IE8 will ignore the UTF-8 option, and will fail completely if the UTF-8 option comes before the Latin-1 option.



                Flask 1.0 supports calling send_file with Unicode filenames. If you use Flask 1.0, you can use send_file with as_attachment=True and a Unicode filename.



                from flask import send_file

                @app.route('/send-python-report')
                def send_python_report():
                return send_file('python_report.html', as_attachment=True)


                Until then, you can construct the header manually using the same process Flask will use.



                import unicodedata

                from flask import send_file
                from werkzeug.urls import url_quote

                @app.route('/send-python-report')
                def send_python_report():
                filename = 'python_report.html'
                rv = send_file(filename)

                try:
                filename = filename.encode('latin-1')
                except UnicodeEncodeError:
                filenames = {
                'filename': unicodedata.normalize('NFKD', filename).encode('latin-1', 'ignore'),
                'filename*': "UTF-8''{}".format(url_quote(filename)),
                }
                else:
                filenames = {'filename': filename}

                rv.headers.set('Content-Disposition', 'attachment', **filenames)
                return rv




                For security you should use send_from_directory instead if the filename is provided by user input. The process is the same as above, substituting the function.





                WSGI doesn't ensure the order of header options, so if you want to support IE8 you must construct the header value completely manually using dump_options_header with an OrderedDict. Otherwise, filename* may appear before filename, which as noted above does not work in IE8.



                from collections import OrderedDict
                import unicodedata

                from flask import send_file
                from werkzeug.http import dump_options_header
                from werkzeug.urls import url_quote

                @app.route('/send-python-report')
                def send_python_report():
                filename = 'python_report.html'
                rv = send_file(filename)
                filenames = OrderedDict()

                try:
                filename = filename.encode('latin-1')
                except UnicodeEncodeError:
                filenames['filename'] = unicodedata.normalize('NFKD', filename).encode('latin-1', 'ignore')
                filenames['filename*']: "UTF-8''{}".format(url_quote(filename))
                else:
                filenames['filename'] = filename

                rv.headers.set('Content-Disposition', dump_options_header('attachment', filenames))
                return rv






                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Nov 24 '18 at 17:58

























                answered Apr 12 '17 at 17:36









                davidismdavidism

                63.9k12167185




                63.9k12167185
































                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f43365640%2fset-unicode-filename-in-flask-response-header%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    404 Error Contact Form 7 ajax form submitting

                    How to know if a Active Directory user can login interactively

                    TypeError: fit_transform() missing 1 required positional argument: 'X'