英文:
Go, extensible system that classify files
问题
我有一个想法,可以创建一个“脚本”,用于遍历目录树对文件进行分类并采取相应的操作(如果是照片,则上传到Flickr;如果是视频,则移动到一个文件夹等)。
我对Go语言没有了解,但我想改变这一点。文件的发现和分类似乎不是最重要的部分。使用协程(routines)和通道(channels)似乎足够实现非阻塞的系统。
我最关心的是要执行的操作。我肯定会编写一些操作,比如“FlickrUpload”或“Shell”(用于mv
、cp
等命令)。但我希望有一个可配置的系统,可以将操作附加到MIME类型上。我认为使用XML结构来配置它并解析起来比较简单。
<?xml version="1.0" standalone="yes">
<configuration>
<file type="image/jpg">
<action name="FlickrUpload" />
<action name="Shell">
mv ${file} /some/absolute/path/
</action>
<!-- 或者 -->
<FlickrUpload />
</file>
</configuration>
但我不知道如何将操作及其参数(来自XML)绑定到一个将成为接口的action
上(除非你有其他建议)。
type action interface {
execute(path string) error
}
英文:
I have the idea to create a "script" that will walk a directory tree to classify files and take actions (If it is a photo, upload to Flickr. If it is a video, move to a folder, ..).
I have no knwoledge of Go but would like to change that. The file discovery and classification don't seems to be the big part. routines and channels seems to be enough to have a non blocking system.
My main concern is more about the actions to take. I will certainly code some actions like "FlickrUpload" or "Shell" (for mv
, cp
, ..). But I would like to have a configurable system where I can attach an action to a mime type. I think an Xml structure could be nice to configure it and seems simple to parse.
<?xml version="1.0" standalone="yes">
<configuration>
<file type="image/jpg">
<action name="FlickrUpload" />
<action name="Shell">
mv ${file} /some/absolute/path/
</action>
<!-- or -->
<FlickrUpload />
</file>
</configuration>
But I have no idea on how to bind the action and his arguments (from Xml) to an action
that will be an interface
(unless you say other).
type action interface {
execute(path string) error
}
答案1
得分: 3
从XML流或任何其他文本媒体中,你只能获取文本。
所以你的 mv ${file} /some/absolute/path/
本质上是文本。
这意味着在实际调用真正的操作之前,你必须不可避免地对其进行解析。
现在你有这个问题的两个方面:
- 如何在知道操作后调用特定的Go代码。
- 如何将“操作参数”数据传递给它。
它们有些交织在一起,取决于你如何决定解决(1)。
调用Go函数的第一种方法是,通过其文本“句柄”,简单地将这些句柄映射到函数,就像这样:
type Action func (...interface{}) error
var actions = map[string]Action {
"mv": Move,
"flickr": UploadToFlickr,
}
func Move(args ...interface{}) error {
// 实现
}
func UploadToFlickr(args ...interface{}) error {
// 实现
}
现在你可以像这样调度你的操作:
act, ok := actions[action_name]
if !ok {
log.Fatalf("配置错误:不支持的操作:%s\n", action_name)
}
err := act(action_args...)
if err != nil {
// 处理
}
注意这些
...
部分:它们实现了可变参数函数,并允许将值切片传递给可变参数函数。
第二种方法是更严格地使用类型:
-
你定义一个接口:
type Action interface { func Do(fname string) error }
-
你的配置解析代码将产生具体类型的值,比如
MoveAction
、FlickrUploadAction
。所有这些类型都必须实现接口
Action
的Do()
方法。这样做,它们就自动实现了该接口。
这也意味着你的配置解析器可以返回该接口的值,就像这样:func ConfigParser() (map[string]Action, error)
...其中
map[string]Action
将文件的MIME类型映射到具体类型的值,比如对于类型为image/png
的文件,映射到FlickrUploadAction
,对于类型为text/plain
的文件,映射到MoveAction
。
要调用一个操作,你可以使用那个想象中的 ConfigParser()
函数产生的映射,使用文件的MIME类型查找并获取一个实现了 Action
接口的值,然后剩下的就是简单地调用它的 Do()
方法,将实际文件的名称传递给它。
TL;DR
详细了解接口——它们在标准库中非常强大和普遍存在(看看那些 io.Reader
和 io.Writer
)。
英文:
From an XML stream or any other textual medium, you can only obtain text.
So your mv ${file} /some/absolute/path/
is essentially text.
This means you will inevitably need to parse it before actually call
your real action on whatever you have parsed out.
Now you have two facets of this problem:
- How to call certain Go code once an action is known.
- How to pass "action arguments" data to it.
They are intertwined a bit—depending on how do you decide to solve (1).
The first approach to call Go function given its textual "handle" is to
simply have a map of such handles to functions, like this:
type Action func (...interface{}) error
var actions = map[string]Action {
"mv": Move,
"flickr": UploadToFlickr,
}
func Move(args ...interface{}) error {
// implement
}
func UploadToFlickr(args ...interface{}) error {
// implement
}
And now you dispatch your actions like
act, ok := actions[action_name]
if !ok {
log.Fatalf("Config error: Unsupported action: %s\n', action_name)
}
err := act(action_args...)
if err != nil {
// Handle
}
> Notice these ...
bits: they implement variadic functions
> and allow to pass a slice of values to a variadic function.
The second approach is to go more strictly typed:
-
You define an interface:
type Action interface { func Do(fname string) error }
-
Your configuration parsing code would produce values of concrete types
like, say,MoveAction
,FlickrUploadAction
.All these types must implement a method
Do()
of the interfaceAction
.By doing this, they automagically implment the said interface.
This also means your condiguration parser could return values of that
interface—something like this:func ConfigParser() (map[string]Action, error)
…where
map[string]Action
would map MIME types of files
to values of concrete types—such asFlickrUploadAction
for files of typeimage/png
andMoveAction
for files of type
text/plain
.
To call an action, you lookup a map produced by that imaginary
ConfigParser()
function using the file's MIME type and obtain a value
which implements the Action
interface, so what's left is simply
calling its Do()
method passing it the name of the actual file.
TL;DR
Read up on interfaces—they are super powerful and pervasive
in the standard library (look at those io.Reader
s and io.Writer
s).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论