dart-archive / shelf_static

archived repo
https://github.com/dart-lang/shelf/tree/master/pkgs/shelf_static
BSD 3-Clause "New" or "Revised" License
24 stars 24 forks source link

createFileHandler for video file #58

Closed mengyanshou closed 3 years ago

mengyanshou commented 3 years ago

I've tried to use createFileHandler for video file. and i can't buffer the video in the browser,It only plays when I download it completely. but i use virtual_directory from httt_server work normally, i think maybe the reason is response doesn't containes content-range

mengyanshou commented 3 years ago

i edit _handleFile like this

Future<Response> _handleFile(Request request, File file,
    FutureOr<String> Function() getContentType) async {
  final stat = file.statSync();
  final ifModifiedSince = request.ifModifiedSince;

  if (ifModifiedSince != null) {
    final fileChangeAtSecResolution = toSecondResolution(stat.modified);
    if (!fileChangeAtSecResolution.isAfter(ifModifiedSince)) {
      return Response.notModified();
    }
  }

  final headers = {
    HttpHeaders.contentLengthHeader: stat.size.toString(),
    HttpHeaders.lastModifiedHeader: formatHttpDate(stat.modified)
  };

  final contentType = await getContentType();
  int length = await file.length();
  var range = request.headers[HttpHeaders.rangeHeader];
  if (contentType != null) headers[HttpHeaders.contentTypeHeader] = contentType;
  if (range != null) {
    // We only support one range, where the standard support several.
    var matches = RegExp(r"^bytes=(\d*)\-(\d*)$").firstMatch(range);
    // If the range header have the right format, handle it.
    if (matches != null && (matches[1].isNotEmpty || matches[2].isNotEmpty)) {
      // Serve sub-range.
      int start; // First byte position - inclusive.
      int end; // Last byte position - inclusive.
      if (matches[1].isEmpty) {
        start = length - int.parse(matches[2]);
        if (start < 0) start = 0;
        end = length - 1;
      } else {
        start = int.parse(matches[1]);
        end = matches[2].isEmpty ? length - 1 : int.parse(matches[2]);
      }
      // If the range is syntactically invalid the Range header
      // MUST be ignored (RFC 2616 section 14.35.1).
      if (start <= end) {
        if (end >= length) {
          end = length - 1;
        }
        if (start >= length) {
          return Response(HttpStatus.requestedRangeNotSatisfiable);
        }

        // Override Content-Length with the actual bytes sent.
        headers[HttpHeaders.contentLengthHeader] = (end - start + 1).toString();

        // Set 'Partial Content' status code.
        headers[HttpHeaders.contentRangeHeader] = 'bytes $start-$end/$length';

        // Pipe the 'range' of the file.
        if (request.method == 'HEAD') {
          return Response(
            HttpStatus.partialContent,
            body: '',
            headers: headers,
          );
        } else {
          return Response(
            HttpStatus.partialContent,
            body: file.openRead(start, end + 1),
            headers: headers,
          );
        }
      }
    }
  }
  return Response.ok(file.openRead(), headers: headers);
}

now it worked.

mengyanshou commented 3 years ago

and Resume breakpoint supported

kevmoo commented 3 years ago

Pull request welcome!

kevmoo commented 3 years ago

I'm going to close this as duplicate of https://github.com/dart-lang/shelf_static/issues/3

But please send a PR!