Moodle API文档 —— File API

概述

Moodle File API中介绍了Moodle管理文件的相关内容。如果你对该API的内部运行机制感兴趣,我们会在接下来的文档中介绍。本文档仅介绍File API的使用。和本API相关的还有Repository API,它介绍了用户如何从moodle中获取文件。

文件区

文件被存储在一个被称为文件区(File Areas)的结构中。一个文件由以下内容唯一标识:

  • 上下文id(context id
  • 完整的组件名(component name)(使用Frankenstyle,在之后的文档中会介绍),例如’course’, ‘mod_forum’, ‘mod_glossary’, ‘block_html’。
  • 文件区类型(file area type),例如’intro’,’post’。
  • 一个唯一的项目id(itemid),通常项目id和一些依赖于文件区类型的东西相关。

Moodle没有单独为文件区开辟空间来进行存储,它被隐式的存储在数据库的files表中。每一个子系统都只能访问它自己的文件区。例如,只有位于/mod/assignment/*目录下的文件可以访问组件名为mod_assignment的文件。

文件区的命名

Moodle对于文件区的命名规范并没有严格的定义,但是我们强烈推荐来使用常见的单数名词来为文件区命名。(intro, post, attachment, description, …)

向用户提供文件

在使用文件时,你必须提供包括文件服务脚本(file serving script)在内的文件url,文件服务脚本通常为pluginfile.php。格式如下:

1
$url = $CFG->wwwroot/pluginfile.php/$contextid/$component/$filearea/arbitrary/extra/infomation.ext

例如:

1
$url = $CFG->wwwroot/pluginfile.php/$forumcontextid/mod_forum/post/$postid/image.jpg

不过,一般来说你没有必要使用这种直接写出url的方式。Moodle提供了名为moodle_url::make_pluginfile_url()的函数来获取文件的url。

1
$url = moodle_url::make_pluginfile_url($file->get_contextid(), $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename());

注意:如果你不需要’itemid’,那么你需要给该参数传入null,此时在url中会忽略itemid的信息。在下面提到的回调函数中,你也需要注意这个问题。

文件服务脚本会根据相应的安全规范检查上下文id,组件名,文件区类型,并基于此提供文件服务。

注意:在多数情况下,开发者开发第三方你插件时,pluginfile.php会在合适的插件中寻找回调函数。这些函数定义在lib.php中,并且以component_name_pluginfile()的形式命名。arbitrary/extra/infomation.ext会被传给回调函数。例如,位于mod_forum+post文件区的文件通过调用位于mod/forum/lib.php文件下的mod_forum_pluginfile()函数来提供文件服务。开发者的第三方插件中,MYPLUGIN/lib.php文件下的相应函数应参照如下模式书写。当然,细节方面会由于相应插件的文件访问权限不同而有所区别。(例如,作业附件只能被教师访问,而学生只能提交文件。讨论区中的附件只有在打开相应的帖子时才能被访问)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
function MYPLUGIN_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) {
// Check the contextlevel is as expected - if your plugin is a block, this becomes CONTEXT_BLOCK, etc.
if ($context->contextlevel != CONTEXT_MODULE) {
return false;
}

// Make sure the filearea is one of those used by the plugin.
if ($filearea !== 'expectedfilearea' && $filearea !== 'anotherexpectedfilearea') {
return false;
}

// Make sure the user is logged in and has access to the module (plugins that are not course modules should leave out the 'cm' part).
require_login($course, true, $cm);

// Check the relevant capabilities - these may vary depending on the filearea being accessed.
if (!has_capability('mod/MYPLUGIN:view', $context)) {
return false;
}

// Leave this line out if you set the itemid to null in make_pluginfile_url (set $itemid to 0 instead).
$itemid = array_shift($args); // The first item in the $args array.

// Use the itemid to retrieve any relevant data records and perform any security checks to see if the
// user really does have access to the file in question.

// Extract the filename / filepath from the $args array.
$filename = array_pop($args); // The last item in the $args array.
if (!$args) {
$filepath = '/'; // $args is empty => the path is '/'
} else {
$filepath = '/'.implode('/', $args).'/'; // $args contains elements of the filepath
}

// Retrieve the file from the Files API.
$fs = get_file_storage();
$file = $fs->get_file($context->id, 'mod_MYPLUGIN', $filearea, $itemid, $filepath, $filename);
if (!$file) {
return false; // The file does not exist.
}

// We can now send the file back to the browser - in this case with a cache lifetime of 1 day and no filtering.
// From Moodle 2.3, use send_stored_file instead.
send_file($file, 86400, 0, $forcedownload, $options);
}

你可以使用一个API函数来自动生成这些url,通常为file_rewrite_pluginfile_urls()函数。

获取用户提交的文件

在之后的文档中会有介绍

代码范例

需要注意的是,开发者们在不触及到moodle核心的情况下,通常不会直接使用文件API,表单元素(formslib elements)会自动帮助开发者解决这些问题。

浏览文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$browser = get_file_browser();
$context = get_system_context();

$filearea = null;
$itemid = null;
$filename = null;
if ($fileinfo = $browser->get_file_info($context, $component, $filearea, $itemid, '/', $filename)) {
// build a Breadcrumb trail
$level = $fileinfo->get_parent();
while ($level) {
$path[] = array('name'=>$level->get_visible_name());
$level = $level->get_parent();
}
$path = array_reverse($path);
$children = $fileinfo->get_children();
foreach ($children as $child) {
if ($child->is_directory()) {
echo $child->get_visible_name();
// display contextid, itemid, component, filepath and filename
var_dump($child->get_params());
}
}
}

移动文件

例如,如果你刚刚用如下路径创建了一个文件

1
2
$from_zip_file = $CFG->dataroot . '/temp/backup/' . $preferences->backup_unique_code .
'/' . $preferences->backup_name;

而你希望将它移动到course_backup文件区中,那么只需要

1
2
3
4
5
6
$context = get_context_instance(CONTEXT_COURSE, $preferences->backup_course);
$fs = get_file_storage();
$file_record = array('contextid'=>$context->id, 'component'=>'course', 'filearea'=>'backup',
'itemid'=>0, 'filepath'=>'/', 'filename'=>$preferences->backup_name,
'timecreated'=>time(), 'timemodified'=>time());
$fs->create_file_from_pathname($file_record, $from_zip_file);

列出文件区的文件

1
2
3
4
5
6
$fs = get_file_storage();
$files = $fs->get_area_files($contextid, 'mod_assignment', 'submission', $submission->id);
foreach ($files as $f) {
// $f is an instance of stored_file
echo $f->get_filename();
}

或以链接的方式列出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$out = array();

$fs = get_file_storage();
$files = $fs->get_area_files($contextid, 'mod_assignment', 'submission', $submission->id);

foreach ($files as $file) {
$filename = $file->get_filename();
$url = moodle_url::make_file_url('/pluginfile.php', array($file->get_contextid(), 'mod_assignment', 'submission',
$file->get_itemid(), $file->get_filepath(), $filename));
$out[] = html_writer::link($url, $filename);
}
$br = html_writer::empty_tag('br');

return implode($br, $out);

创建文件

下面给出了创建包含制定文本的文件的方式。这种方式和使用PHP函数file_put_contents是等价的。

1
2
3
4
5
6
7
8
9
10
11
12
13
$fs = get_file_storage();

// Prepare file record object
$fileinfo = array(
'contextid' => $context->id, // ID of context
'component' => 'mod_mymodule', // usually = table name
'filearea' => 'myarea', // usually = table name
'itemid' => 0, // usually = ID of row in table
'filepath' => '/', // any path beginning and ending in /
'filename' => 'myfile.txt'); // any filename

// Create file containing text 'hello world'
$fs->create_file_from_string($fileinfo, 'hello world');

如果你想基于一个真实的文件在moodle系统中创建文件,你可以使用create_file_from_pathname()函数。同样的,你也可以使用create_file_from_storedfile()函数来创建已经存在于moodle本地文件中的文件。一些细节可以浏览lib/filestorage/file_storage.php

和普通方法不同,这种方法不会覆盖掉已经存在的文件,如果你希望覆盖掉它,你只有先将其删除(如果存在的话)再重新创建该文件。

读取文件

下面给出了一种读取文件的方式,这和file_get_contents()函数是等价的。

注意:你只能在位于mod/mymodule/*目录下的代码中使用这种方法,其他代码中应使用file_browser来读取文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$fs = get_file_storage();

// Prepare file record object
$fileinfo = array(
'component' => 'mod_mymodule', // usually = table name
'filearea' => 'myarea', // usually = table name
'itemid' => 0, // usually = ID of row in table
'contextid' => $context->id, // ID of context
'filepath' => '/', // any path beginning and ending in /
'filename' => 'myfile.txt'); // any filename

// Get file
$file = $fs->get_file($fileinfo['contextid'], $fileinfo['component'], $fileinfo['filearea'],
$fileinfo['itemid'], $fileinfo['filepath'], $fileinfo['filename']);

// Read contents
if ($file) {
$contents = $file->get_content();
} else {
// file doesn't exist - do something
}

你不能直接访问位于磁盘中的文件,而应在临时文件区中创建该文件的备份,之后再访问该文件。创建备份可以使用$file->copy_content_to($pathname)

删除文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$fs = get_file_storage();

// Prepare file record object
$fileinfo = array(
'component' => 'mod_mymodule',
'filearea' => 'myarea', // usually = table name
'itemid' => 0, // usually = ID of row in table
'contextid' => $context->id, // ID of context
'filepath' => '/', // any path beginning and ending in /
'filename' => 'myfile.txt'); // any filename

// Get file
$file = $fs->get_file($fileinfo['contextid'], $fileinfo['component'], $fileinfo['filearea'],
$fileinfo['itemid'], $fileinfo['filepath'], $fileinfo['filename']);

// Delete it if it exists
if ($file) {
$file->delete();
}
文章目录
  1. 1. 概述
  2. 2. 文件区
  3. 3. 文件区的命名
  4. 4. 向用户提供文件
  5. 5. 获取用户提交的文件
  6. 6. 代码范例
    1. 6.1. 浏览文件
    2. 6.2. 移动文件
    3. 6.3. 列出文件区的文件
    4. 6.4. 创建文件
    5. 6.5. 读取文件
    6. 6.6. 删除文件