使用文件系统作为离线播放的视频源

Using File System as source of videos for playing offline

我正在尝试为我的 HTML5 视频播放器添加离线功能。我试图将文件作为 blob 写入 chrome 文件系统,然后从那里读取它们。我相信我 运行 遇到了一个问题,文件实际上没有被写入,只是文件名。由于我的以下代码目前已构成,它可以工作,但仍然只有在它永久连接到互联网时才有效。我的目标是将文件下载到文件系统中的持久目录,然后在互联网断开时继续播放。

$(document).ready(function() {


    var dir = "http://www.kevmoe.com/networks/gsplayer/";
    var fileextension = ".mp4";
    var srcfiles = $.ajax({
        //This will retrieve the contents of the folder if the folder is configured as 'browsable'
        url: dir,
        success: function(data) {
            //List all .mp4 file names in the page
            $(data).find("a:contains(" + fileextension + ")").each(function() {
                var filename = $(this).attr("href").replace(window.location.host, "").replace("http://", "");

                $("#container").append("<div id='div1' class='video'><video id='video1' class='vidarray' preload='none' poster='bkg.png'><source src='" + filename + "' type='video/mp4'></video></div>");
                async: false;


                window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;

                window.requestFileSystem(window.PERSISTANT, 200000 * 1024 * 1024, initFS, errorHandler);

                function initFS(fs) {
                    console.log('filesystem engaged'); // Just to check if everything is OK :)
                    // place the functions you will learn bellow here
                    function errorHandler(err) {
                        var msg = 'An error occured: ';
                    };

                    function createDir(rootDir, folders) {
                        rootDir.getDirectory(folders[0], {
                            create: true
                        }, function(dirEntry) {
                            if (folders.length) {
                                createDir(dirEntry, folders.slice(1));
                            }
                        }, errorHandler);
                    };

                    createDir(fs.root, 'files/video/'.split('/'));

                    fs.root.getDirectory('video', {}, function(dirEntry) {
                        var dirReader = dirEntry.createReader();
                        dirReader.readEntries(function(entries) {
                            for (var i = 0; i < entries.length; i++) {
                                var entry = entries[i];
                                if (entry.isDirectory) {
                                    console.log('Directory: ' + entry.fullPath);
                                } else if (entry.isFile) {
                                    console.log('File: ' + entry.fullPath);
                                }
                            }

                        }, errorHandler);
                    }, errorHandler);

                    fs.root.getFile(filename, {
                        create: true,
                        exclusive: true
                    }, function(fileEntry) {
                        fileEntry.createWriter(function(fileWriter) {
                            var blob = new Blob([data], {
                                type: 'video/mp4'
                            });
                            fileWriter.write(blob);
                        }, errorHandler);

                        console.log('file downloaded');
                    }, errorHandler);

                    //Try to add an event listener for when all files are finished loading into file system. Then use another function to source the videos locally.
                    var dirReader = fs.root.createReader();
                    var entries = [];

                    // Call the reader.readEntries() until no more results are returned.

                    dirReader.readEntries(function(results) {

                        //List all .mp4 file names in the page
                        $(results).find("a:contains(" + fileextension + ")").each(function() {
                            var filename = $(this).attr("href").replace(window.location.host, "").replace("http://", "");

                            $("#container").append("<div id='div1' class='video'><video id='video1' class='vidarray' preload='none' poster='bkg.png'><source src='" + filename + "' type='video/mp4'></video></div>");
                            async: false;

                        }, errorHandler);
                    });
                };

                function errorHandler() {
                    console.log('An error occured');
                };
            });


            var videos = $('.video');
            //handle ending of video
            videos.find('video').on('ended', function() {
                playNextVideo(videos);
            });

            // start with the first one
            playNextVideo(videos);


            function playNextVideo(videoList) {
                var activeVideo = videoList.filter('.active').removeClass('active'), // identify active video and remove active class
                    activeIndex = videoList.index(activeVideo), // get the active video index in the group
                    nextVideo = videoList.eq(activeIndex + 1), // get the next video in line
                    actualVideo;

                // if there is no next video start from first
                if (nextVideo.length == 0) nextVideo = videoList.first();

                // pause all videos
                videoList.find('video').each(function() {
                    this.pause();
                })

                // get reference to next video element
                actualVideo = nextVideo.find('video').get(0);

                // add active class to next video
                nextVideo.addClass('active');

                // load and play
                actualVideo.volume = 0.04;
                actualVideo.load();
                actualVideo.play();
            }
        }
    });
});

您的用户必须授予您的应用在本地存储数据的权限,然后您的应用才能使用持久存储。 这就是为什么您必须先申请配额。您要求的字节数是 200000 * 1024 * 1024 字节。

window.storageInfo.requestQuota(PERSISTENT, 200000 * 1024 * 1024, 
    function(grantedBytes) {
        window.requestFileSystem(window.PERSISTENT, grantedBytes, onInitFs, errorHandler);
    }, 
    errorHandler
);

MDN documentation

我注意到您是为 Chrome、here's how you manage the quota in Chrome

写的

filesystem: 协议存储与 document 请求 LocalFileSystem 相同来源的文件。也就是说,如果 JavaScript at Question 是在例如 http://example.org 处创建的,那么到 LocalFileSystem 的路径应该与 http://example.org 相同,而不是 file: 协议.

如果您尝试存储文件或文件夹以供离线访问 file: 协议,您可以创建一个 .html 文档用作模板书签。

在线访问本地 .html 文件一次以获取文件并填充 LocalFileSystem。如果 navigator.onLinetrue,导航到 http://example.org,否则获取并处理存储在 LocalFileSystem.

的文件和文件夹

创建一个列表作为 JSON 或 JavaScript Array 来存储要获取的文件列表,而不是解析文件位置的 .html document .

将本地文件存储为书签。启动 Chromium,Chrome 并设置 --allow-file-access-from-files 标志以从 file: 协议访问 filesystem: 协议,如果不在线,则在 filesystem: 协议访问 file: 协议。

<!DOCTYPE html>
<html>
<head>
  <title>LocalFileSystem Offline Videos Bookmark</title>
</head>
<body>
<script>

// location to visit if online
const onLineURL = "https://lorempixel.com/" 
                  + window.innerWidth 
                  + "/" 
                  + window.innerHeight + "/cats";

const props = {
  requestedBytes: 1024 * 1024 * 20000,
  folder: "videos",
  // list of files to fetch for offline viewing
  mediaList: [
    "http://mirrors.creativecommons.org/movingimages/webm/"
    + "ScienceCommonsJesseDylan_240p.webm"
  , "https://nickdesaulniers.github.io/netfix/demo/frag_bunny.mp4"
  ]
};

let grantedBytes = 0;

function getLocalFileSystem ({requestedBytes = 0, mediaList=[], folder = ""}) {
    if (!requestedBytes || !mediaList.length || !folder) {
      throw new Error("requestedBytes: Number"
                     + " or mediaList: Array"
                     + " or folder: String not defined");
    };
    // do stuff with `filesystem:` URL
    function processLocalFilePath(localPath) {
        const video = document.createElement("video");
        document.body.appendChild(video);
        video.controls = true;
        video.src = localPath;
    }

    function errorHandler(err) {
        console.log(err);
    }

    function writeFile(dir, fn, fp, localPath) {
        console.log(dir, fn, fp, localPath);
        dir.getFile(fn, {}, function(fileEntry) {
            fileEntry.createWriter(function(fileWriter) {
                fileWriter.onwriteend = function(e) {
                    // do stuff when file is written
                    console.log(e.type, localPath + " written");
                    window.webkitResolveLocalFileSystemURL(localPath
                    , function(file) {
                        // file exists in LocalFileSystem
                        processLocalFilePath(localPath);
                    }, errorHandler)
                };

                fileWriter.onerror = errorHandler;
                fetch(fp).then(function(response) {
                    return response.blob()
                }).then(function(blob) {
                    fileWriter.write(blob);
                }).catch(errorHandler)
            }, errorHandler);
        }, errorHandler);
    }

    if (mediaList && mediaList.length) {
        navigator.webkitTemporaryStorage.requestQuota(requestedBytes
        , function(grantedBytes_) {
            grantedBytes = grantedBytes_;
            console.log("Requested bytes:", requestedBytes
                       , "Granted bytes:", grantedBytes);
            window.webkitRequestFileSystem(window.TEMPORARY
            , grantedBytes
            , function(fs) {

                const url = fs.root.toURL();

                mediaList.forEach(function(filename) {

                    const localPath = url + folder + "/" 
                                      + filename.split("/").pop();

                    window.webkitResolveLocalFileSystemURL(localPath
                    , function(file) {
                        // file exists in LocalFileSystem
                        console.log(localPath + " exists at LocalFileSystem");
                        processLocalFilePath(localPath)

                    }, function(err) {
                        console.log(err, localPath 
                        + " not found in LocalFileSystem");
                        // Exception is thrown if file 
                        // or folder path not found
                        // create `folder` directory, get files
                        fs.root.getDirectory(folder, {}
                        , function(dir) {
                            writeFile(dir
                            , filename.split("/").pop()
                            , filename
                            , localPath);
                        }),
                        errorHandler
                    })
                })

            })
        }, errorHandler)
    }
}

if (location.href !== onLineURL && navigator.onLine) {
    location.href = onLineURL;
} else {
    getLocalFileSystem(props);
}

</script>
</body>
</html>

另见

  • How to use webkitRequestFileSystem at file: protocol

  • Read local XML with JS

  • How to Write in file (user directory) using JavaScript?


另一种方法是利用 ServiceWorker