Scratch二次开发6:如何保存作品到自己的服务器

写在前面:

为公共事业做贡献,做了个开源版本:scratch.lite

开源版本带MySQL后台服务器,功能:注册、登录、保存作品、分享、修改作品名称、保存作品缩略图。

有兴趣的朋友可以去下载参考:lite: 一个轻量级的Scratch编程分享平台:注册登录、作品创作、作品管理、素材管理、用户管理,作品点赞、收藏、分享。

Scratch二次开发的纯技术交流QQ群:115224892/914159821

  • 一、关于作品形态

1、一个单独的文件保存,如:.sb/.sb2/.sb3;

2、分开保存:源代码以JSON格式保存到数据库,素材以文件形式保存。

两种方式的对比分析:

第一种操作简单,但作品的素材被打包进文件,源文件会比较大;(个人平台可以用用)

第二种技术上要多走几步。素材可以被简单的重复使用。(作品量大时,推荐使用)

本文重点介绍第二种方式。

  • 二、一个Scratch3作品JSON格式分析

源代码中,自带了一个默认作品:/src/lib/default-project/project-data.js

想简单修改默认作品的小猫图片或是替换默认作品的,可以在此操作,直接使用。

{
        targets: [
            {
                isStage: true,
                name: 'Stage',
                variables: {
                    '`jEk@4|i[#Fk?(8x)AV.-my variable': [
                        translator(messages.variable),
                        0
                    ]
                },
                lists: {},
                broadcasts: {},
                blocks: {},
                currentCostume: 0,
                costumes: [
                    {
                        assetId: 'cd21514d0531fdffb22204e0ec5ed84a',
                        name: translator(messages.backdrop, {index: 1}),
                        md5ext: 'cd21514d0531fdffb22204e0ec5ed84a.svg',
                        dataFormat: 'svg',
                        rotationCenterX: 240,
                        rotationCenterY: 180
                    }
                ],
                sounds: [
                    {
                        assetId: '83a9787d4cb6f3b7632b4ddfebf74367',
                        name: translator(messages.pop),
                        dataFormat: 'wav',
                        format: '',
                        rate: 11025,
                        sampleCount: 258,
                        md5ext: '83a9787d4cb6f3b7632b4ddfebf74367.wav'
                    }
                ],
                volume: 100
            },
            {
                isStage: false,
                name: translator(messages.sprite, {index: 1}),
                variables: {},
                lists: {},
                broadcasts: {},
                blocks: {},
                currentCostume: 0,
                costumes: [
                    {
                        assetId: 'b7853f557e4426412e64bb3da6531a99',
                        name: translator(messages.costume, {index: 1}),
                        bitmapResolution: 1,
                        md5ext: 'b7853f557e4426412e64bb3da6531a99.svg',
                        dataFormat: 'svg',
                        rotationCenterX: 48,
                        rotationCenterY: 50
                    },
                    {
                        assetId: 'e6ddc55a6ddd9cc9d84fe0b4c21e016f',
                        name: translator(messages.costume, {index: 2}),
                        bitmapResolution: 1,
                        md5ext: 'e6ddc55a6ddd9cc9d84fe0b4c21e016f.svg',
                        dataFormat: 'svg',
                        rotationCenterX: 46,
                        rotationCenterY: 53
                    }
                ],
                sounds: [
                    {
                        assetId: '83c36d806dc92327b9e7049a565c6bff',
                        name: translator(messages.meow),
                        dataFormat: 'wav',
                        format: '',
                        rate: 22050,
                        sampleCount: 18688,
                        md5ext: '83c36d806dc92327b9e7049a565c6bff.wav'
                    }
                ],
                volume: 100,
                visible: true,
                x: 0,
                y: 0,
                size: 100,
                direction: 90,
                draggable: false,
                rotationStyle: 'all around'
            }
        ],
        meta: {
            semver: '3.0.0',
            vm: '0.1.0',
            agent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36' // eslint-disable-line max-len
        }
    }

从默认作品的JSON数据就可以看到其组成,不再赘述。

默认作品资源的获取:/src/lib/default-project/index.js

[{
        id: 0,
        assetType: 'Project',
        dataFormat: 'JSON',
        data: JSON.stringify(projectJson)
    }, {
        id: '83a9787d4cb6f3b7632b4ddfebf74367',
        assetType: 'Sound',
        dataFormat: 'WAV',
        data: new Uint8Array(popWav)
    }, {
        id: '83c36d806dc92327b9e7049a565c6bff',
        assetType: 'Sound',
        dataFormat: 'WAV',
        data: new Uint8Array(meowWav)
    }, {
        id: 'cd21514d0531fdffb22204e0ec5ed84a',
        assetType: 'ImageVector',
        dataFormat: 'SVG',
        data: encoder.encode(backdrop)
    }, {
        id: 'b7853f557e4426412e64bb3da6531a99',
        assetType: 'ImageVector',
        dataFormat: 'SVG',
        data: encoder.encode(costume1)
    }, {
        id: 'e6ddc55a6ddd9cc9d84fe0b4c21e016f',
        assetType: 'ImageVector',
        dataFormat: 'SVG',
        data: encoder.encode(costume2)
    }]

从其中可以看出,所有素材的命名规则(md5命名规则,这样可以确保一个素材只保存一份)及获取方式。

  • 三、作品保存到服务器的步骤

1、获取作品JSON源代码

2、素材分析与保存

3、JSON源代码保存

4、作品缩略图保存

触发一个作品的保存动作,有两种方式:1、定时自动保存,、手动点击按钮保存。

这两种方式所做的事,在REACT框架下,其实就是简单的修改并分发作品需要被保存的消息。

真正保存的部分,并不在此,所以这两种方式的源代码,就不再贴了。

保存功能部分所在文件:/src/lib/project-saver-hoc.jsx

        componentDidUpdate (prevProps) {
            //点击保存作品时,保存作品源代码、素材
            //条件已在点击保存作品时判断过:已登录、不是其他用户的作品、内容已变更
            if (this.props.isUpdating && !prevProps.isUpdating) {
                //console.warn("开始保存作品到服务器!!!");
                this.props.onShowInlineAlerts('正在保存作品……');

                return this.storeProject(this.props.reduxProjectId)
                .then(() => {
                    this.props.onUpdatedProject(this.props.loadingState);//更新作品状态
                    this.props.onShowInlineAlerts('作品已保存');//更新作品成功的提示
                })
                .catch(err => {
                    this.props.onShowStandardAlert('作品未能保存');//更新作品失败的提示
                    this.props.onProjectError(err);//
                });
            }

        }

其中使用的Alert提示功能,本人已摒弃了国际化,所以已简化成可以直接写提示内容。

上面的源代码只是整个保存的入口及总流程,真正保存作品的代码如下:

        storeProject (projectId, requestParams) {
            requestParams = requestParams || {};
            if (projectId==0){//新作品,同时上传作品名称
                requestParams['title']=this.props.reduxProjectTitle;
            }

            //在保存作品JSON源代码前,先将素材保存到服务器。
            //这样可以确保在保存作品的过程中素材不必更新
            const projectData = this.props.vm.toJSON();
            return Promise.all(this.props.vm.assets
                .filter(asset => !asset.clean)
                    .map(
                        asset => storage.store(
                            asset.assetType,
                            asset.dataFormat,
                            asset.data,
                            asset.assetId
                        ).then(response => {
                            if (response.status !== 'ok') {
                                  return Promise.reject(response.code);
                            }
                            asset.clean = true;
                        })
                    )
                )
                .then(() =>saveProjectToServer(projectId, projectData, requestParams))
                .then(response => {
                    if (response.id){//返回的作品ID
                        //保存缩略图
                        this.storeProjectThumbnail(response.id);

                        //如果是第一次保存作品
                        if (projectId==0){
                            //第一步:设置作品新Id、作者Id
                            this.props.onSetProjectNewId(this.props.authorId, response.id);
                            //第二步:设置浏览器的uri
                            window.location.hash=response.id;
                        }
                    }
                    
                    this.props.onSetProjectUnchanged();//设置作品为未修改状态
                    return response;
                })
                .catch(err => {
                    log.error(err);
                    throw err; // pass the error up the chain
                });
        }

服务器端源代码,这里只放保存JSON到数据库部分,保存素材、保存缩略图非常简单,就不贴了。

  //保存作品:源代码。req.body为项目JSON源代码
  this.app.put('/scratch/projects/:projectid',function(req, res) {
    console.log('服务器:保存作品JSON源代码');

    var UPDATE =`UPDATE scratch SET src=? WHERE id=${req.params.projectid} LIMIT 1`;
    var VAL = [`${JSON.stringify(req.body)}`];//防注入
    DB.qww(UPDATE, VAL, function(err,SCRATCH){
        if (err) {
            res.status(404).send({});
            return;
        }

        res.status(200).json({"status":"ok"})
    })
  });

预告:下篇博客内容是:Scratch3.0作品三大类状态及对应源代码分析

后续准备写的几个方向:

Scatch3.0一个作品的获取、加载、变更、保存、

Scratch3.0作品状态简化及升级、

Scrath GUI版本菜单定制、

Scratch Alert功能的简化及升级、

Scratch教程功能的简化及升级、

Scratch积本定制、

Scratch扩展定制、

Scratch Player定制(脱离ScratchGUI,仅使用VM,把作品转换成H5格式,方便操作与分享展示)

... ...

(想分享的太多了,工作忙,时间紧,慢慢来吧,各位看官可以先收藏或是关注本博主!!!)

写在后面:

如果本文章对您有帮助,请不吝点个赞再走!!!

您的支持,就是本人继续分享的源动力!!!

Bailee 了个Bye!!!

  • 15
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值