Хобрук: Ваш путь к мастерству в программировании

Браузеры закрывают сокет до полной загрузки ответа

У меня есть простой сервер Python, который использует _1 _. Цель состоит не в том, чтобы показать видео на странице html или загрузить видеофайл, а в том, чтобы отобразить видео напрямую в браузере. Вот что у меня есть на данный момент:

import http.server

class SuperHandler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):
        path = self.path
        encodedFilePath = 'file.mp4'

        with open(encodedFilePath, 'rb') as videoFile:
            self.send_response(200)
            self.send_header('Content-type', 'video/mp4')
            self.end_headers()
            self.wfile.write(videoFile.read())
            print('File sent: ' + videoFile.name)

server_address = ('', 8000)
handler_class = SuperHandler
httpd = http.server.HTTPServer(server_address, handler_class)
httpd.serve_forever()

У меня проблема в том, что ответ не содержит полного видео. file.mp4 составляет 50 МБ, но когда я смотрю на вкладку сети в Chrome или Firefox, он говорит, что ответ составляет всего 1 МБ. Есть ли причина, по которой не передается полный файл? Мне нужно добавить какой-то HTTP-заголовок, чтобы это работало?

РЕДАКТИРОВАТЬ:

Теперь это мой код:

server_address = ('', 8000)
handler_class = http.server.SimpleHTTPRequestHandler
httpd = http.server.HTTPServer(server_address, handler_class)

httpd.serve_forever()

Сейчас я использую do_GET SimpleHTTPRequestHandler по умолчанию, но он все еще не работает (хотя теперь ответ составляет 40 МБ / 30 МБ вместо 1 МБ).

Когда я запрашиваю file.mp4 в Chrome, соединение сокета закрывается через ~ 7 секунд (~ 5 секунд в Firefox), что заставляет скрипт выдавать BrokenPipeError: [Errno 32] Broken pipe, , потому что сервер все еще пытается записать оставшуюся часть видеофайла в закрытый сокет.

Итак, мой вопрос: как я могу заставить браузер загружать полный ответ, прежде чем он закроет сокет?

Дополнительная информация

Заголовки HTTP-ответа, отправленные клиенту:

HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.5.0
Date: Mon, 28 Dec 2015 02:36:39 GMT
Content-type: video/mp4
Content-Length: 53038876
Last-Modified: Fri, 25 Dec 2015 02:09:52 GMT

  • Почему вы хотите использовать http.server для обслуживания статических файлов? Вы можете запустить: python3 -mhttp.server для обслуживания файлов в каталоге (при условии, что вы используете его как быстрый и грязный взлом). 25.12.2015
  • Обслуживание статических файлов - это лишь часть того, что я хочу сделать, поэтому мне нужно самому написать код. 25.12.2015
  • Если вы решили использовать здесь http.server, посмотрите, как реализовано http.server.SimpleHTTPRequestHandler (copyfile() считывает файл с диска и отправляет его пользователю). 25.12.2015
  • Где именно можно найти код реализации SimpleHTTPRequestHandler? 25.12.2015
  • Нашел! opensource.apple.com/source/python/ python-3 / python / Lib / 25.12.2015
  • Официальный репозиторий Mercurial находится по адресу https://hg.python.org/cpython/file/tip/Lib/http/server.py 25.12.2015
  • У меня та же проблема, используя только SimpleHTTPRequestHandler. Похоже, что браузер закрывает TCP-соединение до того, как файл будет полностью загружен. Код python вызывает ошибку BrokenPipeError: [Errno 32] Сломанный канал. Однако, когда я использую только SimpleHTTPRequestHandler (без наследования), браузер может загрузить около 30 МБ из 50 МБ, но этого все равно недостаточно. Может быть, есть что-то особенное при отправке больших файлов, например. заголовок http, который я должен добавить. 25.12.2015
  • SimpleHTTPRequestHandler работает (я только что протестировал его на файле размером 1 ГБ с помощью команды curl url | wc -c). Вам нужна помощь в создании HTML-страницы, использующей видеофайл? 25.12.2015
  • Он работает нормально, когда тип запроса - документ - когда файл напрямую загружается на ваш диск браузером. Однако, когда тип запроса - это мультимедиа (например, видео / mp4), который используется для отображения видео непосредственно в браузере, точно так же, как вы можете просматривать PDF-файл в браузере, браузер, кажется, закрывает соединение, поэтому видеофайл не полностью загружен. Я не пытаюсь показать видеофайл в формате html, я пытаюсь показать видео прямо в браузере, используя встроенный проигрыватель. 25.12.2015
  • Кажется, что через ~ 7 секунд Chrome закрывает соединение, а Firefox через ~ 5 секунд. Я тестировал несколько раз, я займусь этим ... 25.12.2015
  • вам следует изменить свой вопрос: как транслировать видеофайл, чтобы его можно было воспроизвести с помощью Chrome, Firefox 25.12.2015
  • Но пытаюсь ли я заниматься потоковой передачей? Я не пытаюсь просто обслуживать видеофайл? Может это то же самое. 27.12.2015
  • Я не знаю, что вы пытаетесь сделать. Что бы вы ни пытались сделать, ваш код не работает для больших файлов (не используйте .read(), который загружает весь файл в память), используйте copyfile, как я уже упоминал выше (для чтения файлов по частям). 27.12.2015
  • Я пытаюсь заставить браузер отображать видео. Я больше не использую приведенный выше код, я просто использую SimpleHTTPRequestHandler по умолчанию. Он по-прежнему вызывает BrokenPipeError: [Errno 32] Broken pipe при доступе к большому .mp4. 27.12.2015
  • Добавьте заголовок Content-Length, чтобы получатель знал, сколько данных ожидать. 28.12.2015
  • Не работает, я уже отправил этот заголовок клиенту. 28.12.2015
  • @maximedupre Интересно, это потому, что файл mp3 слишком велик? 28.12.2015
  • Что считается слишком большим? 28.12.2015

Ответы:


1

Для потоковой передачи видео вы должны поддерживать как минимум запросы диапазона и transfer-encoding: chunked

Насколько я понимаю, http.server не поддерживает это напрямую. Вы, конечно, можете реализовать это на вершине.

В качестве альтернативы используйте простую структуру, например bottle (один файл, уже поддерживает оба) или cherrypy (более надежный, многопоточный и т. Д.)

Вы также можете обойтись без кода Python, например если вы используете nginx

28.12.2015
  • Но пытаюсь ли я заниматься потоковой передачей? Я не пытаюсь просто обслуживать видеофайл? Может это то же самое. 28.12.2015
  • Также почему браузеры загружаются всего за 5/7 секунд? 28.12.2015
  • Я не понимаю, почему мне нужно поддерживать запросы диапазона, поскольку я хочу передать весь файл, и я также не понимаю, почему мне нужно поддерживать кодировку передачи: фрагментировано, поскольку сервер уже предоставляет Content-Length . 28.12.2015
  • @maximedupre захватывает сетевой трафик на эталонном сервере и отправляется оттуда. Диапазон имеет смысл, если пользователь может перейти к определенному времени в видео. Сломанный канал предполагает, что браузеры хотят меньше данных, чем отправлено, что вполне возможно, если вы проигнорируете заголовок диапазона. 29.12.2015
  • ronallo.com/blog/html5-video-everything-i -Необходимо знать, а также раздел буферизации chromium.org/audio-video 29.12.2015

  • 2

    По сути, @qarma прав. Для потоковой передачи видео вам необходимо использовать библиотеку или фреймворк, которые поддерживают как минимум Range: заголовки.

    Но вы не пытаетесь / не транслировать потоковое видео. Браузер делает это за вас. Когда вы возвращаете тип контента video/mp4, браузер, который знает, как транслировать видео, немедленно переключается в режим потоковой передачи. Он останавливает загрузку файла (источник ошибок вашего канала) и перезапускается с заголовком Range:. Для этого он использует существующий код медиаплеера в браузере. Поскольку класс SimpleHTTPServer не поддерживает заголовок Range:, он не обрабатывает ответ должным образом.

    Если вы хотите предотвратить такое поведение потоковой передачи и заставить браузер загружать файл без его воспроизведения, тогда верните заголовок Content-Disposition, заставляя его рассматривать видеофайл как загруженный файл, а не как часть содержимого, которое нужно отобразить.

    Вот код, основанный на вашем первоначальном вопросе, который делает то, что вы хотите:

    import http.server
    
    class SuperHandler(http.server.SimpleHTTPRequestHandler):
        def do_GET(self):
            path = self.path
            encodedFilePath = 'file.mp4'
    
            with open(encodedFilePath, 'rb') as videoFile:
                self.send_response(200)
                self.send_header('Content-type', 'video/mp4')
                self.send_header('Content-Disposition', 'attachment; filename=' + encodedFilePath)
                self.end_headers()
                self.copyfile(videoFile, self.wfile)
    
    server_address = ('', 8000)
    handler_class = SuperHandler
    httpd = http.server.HTTPServer(server_address, handler_class)
    httpd.serve_forever()
    

    Теоретически вы также отправляете обратно Accept-Ranges: none, но Chrome, по крайней мере, похоже, игнорирует это.

    01.01.2016
  • Я до сих пор не понимаю, зачем мне поддерживать заголовки Range. Я всегда хочу, чтобы передавался весь видеофайл, а не только его часть. Возможно, вы говорите так, потому что, как сказал @qarma, пользователь может перейти к определенному времени в видео, но я не думаю, что это имеет какое-либо отношение к моей проблеме (хотя спасибо за подсказку). Когда вы говорите: он останавливает загрузку файла (источник ошибок вашего канала) и перезапускается с заголовком Range:, вы правы, но это происходит только в Chrome (а не в Firefox). И нет, я не хочу скачивать видеофайл, я хочу воспроизвести его в браузере. 02.01.2016
  • Я действительно нашел решение своей проблемы, отправлю его как ответ, но спасибо за помощь. 02.01.2016

  • Новые материалы

    #093 | Моделирование вспышки эпидемии с помощью JavaScript — Часть 3
    TLDR: Я сделал симуляцию вспышки эпидемии, в которую можно поиграть здесь . Мой холст, моя сцена Мой HTML — это всего лишь один div с классом stage, и вот как я настроил на нем свой объект..

    numberToString.js (8kyu 16)
    Алгоритм кодовых войн Проблема Нам нужна функция, которая может преобразовать число в строку. 숫자를 문자열로 변환하는 함수를 작성해라. Решение 01 function numberToString(n) { return n.toString(); }..

    Лучшие практики для быстрого изучения языка программирования
    Изучение языка программирования может быть сложной задачей, но при правильном подходе определенно возможно быстро выучить язык программирования. Хорошее понимание языка программирования может..

    Использование данных из Adobe Analytics в предложениях Adobe Target
    Я уверен, что все видели эти всплывающие окна в интернет-магазинах, которые говорят что-то вроде « 15 человек просматривают этот товар прямо сейчас! » или « 105 человек из Мичигана купили это..

    Машинное обучение и его набор данных в CreateML
    Когда я впервые начал учиться в Apple Developer Academy, у меня был момент неуверенности в моем интересе к машинному обучению. Нужно ли мне сменить карьеру моей мечты с специалиста по данным на..

    Обучение требует воли
    Недавно я прочитал отличную статью Шейна Легга и Джоэла Венесса из DeepMind. http://arxiv.org/pdf/1109.5951v2.pdf В статье «универсальный интеллект» агента π определяется как: Поэтому..

    Безопасность по дизайну делает всех счастливыми
    Заложенная безопасность делает всех счастливыми Если вы никогда не смотрели Louis C.K. рассказать о том, как Все удивительно, и никто не счастлив ; побаловать себя. Сделайте это прямо..