【docker desktop】创建node:18 server

1、创建node18容器

docker run --rm -it -v "D:/AIHUB_workSpace/USC-courses/EE547":/usr/src/app -p 3000:3000 node:18 /bin/bash

如果没有会自动下载node:18的image,然后自动进入container内部,退出会自动删除container,该image大小为990.67MB

注:之后使用npm install也在/usr/src/app下进行,如果在进入container时的默认根目录进行,可能会出现如Tracker "idealTree" already exists的错误

2、查看版本

root@f2fd36a194ca:/# node -v
v18.9.0
root@f2fd36a194ca:/# npm -v
8.19.1

3、创建index.js文件
因为本地文件夹已经mount到了container内部,在windows该文件夹下直接添加文件便能在container内部看到

'use strict';
const http = require('http');
const server = http.createServer(function (req, res) {
 if (req.url == '/') {
 res.writeHead(200, { 'Content-Type': 'text/html' });
 res.write('<html><body>Hello, node.js!</body></html>');
 res.end();
 }
});
server.listen(3000);
console.log('Server started, port 3000');

此时/usr/src/app下应该有index.js文件
到/usr/src/app启动该server

root@f2fd36a194ca:/usr/src/app# node index.js
Server started, port 3000

4、请求该端口
因为映射了端口3000到container内部的3000
因此可以直接打开浏览器

http://127.0.0.1:3000/

便能看到Hello, node.js!的返回结果

或者如果在Windows安装了curl,可以打开cmd,便能有如下结果

C:\Users\ASUS>curl http://127.0.0.1:3000/
<html><body>Hello, node.js!</body></html>

5、使用nodemon

当用该命令启动index.js时,如果nodemon检测到index.js文件有改变,会自动重新启动服务,便于快速开发

在container内部安装

npm install nodemon

如果这行安装不了,用

npm install -g nodemon

因为我们使用nodemon一般是在命令行使用,所以加上-g安装的nodemon才提供在命令行中使用该库的工具,否则就会提示找不到nodemon。如果我们只需要在程序中调用该包,则不用加-g即可

如果不小心关掉了cmd界面用该命令重新进入container

docker exec -it f2fd36a194cad7c294edbcbe04f5ae2bce003cb52c8d55b30f320ead6367af0c /bin/bash

现在用nodemon启动index.js

nodemon index.js

http://127.0.0.1:3000/secret访问服务器,会发现没有反应,也不会自动结束,而是处于等待状态

现在进化一下index.js如下,使之可以返回404

'use strict';
const http = require('http');
const PORT = 3000;
const server = http.createServer((req, res) => {
 if (req.url == '/') {
 res.writeHead(200, { 'Content-Type': 'text/html' });
 res.write('<html><body>Hello, node.js!</body></html>');
 res.end();
 } else {
 res.writeHead(404);
 res.end();
 }
});
server.listen(PORT);
console.log(`Server started, port ${PORT}`);

当我们在Windows端修改了该文件,nodemon会自动检测到文件改变,然后自动重新启动

现在访问不存在的路径会自动返回404

注:自动启动功能在Windows10上结合docker desktop,并没有测试成功,仍需手动重启,可能有一些兼容性原因; 一个StackOverflow上的解决方法是用nodemon --legacy-watch index.js启动,--legacy-watch会将nodemon听(listen)文件系统事件的行为变成轮询(poll),这样理论上会慢一点,但可以保证能够检测到文件的改变并自动重启

6、一个更好的index.js

'use strict';
const http = require('http');

const PORT = 3000;

const server = http.createServer((req, res) => {
    let respBody = '';
    let contentType = 'text/html';
    let httpCode = 404;

    try {
        switch (req.method.toLowerCase()) {
            case 'get':
                // can add logs to see elements
                console.error(req.url)
                const urlPath = req.url.split('/');

                if (req.url === '/') { 
                    httpCode = 200;
                    respBody = 'Welcome home';
                } else if (urlPath.length === 2 && urlPath[1] === 'greet') {
                    httpCode = 200;
                    respBody = '<html><body>Hello, node.js!</body></html>';
                } else if (urlPath.length === 3 && urlPath[1] === 'greet' && urlPath[2].match(/^a-z+$/)) {
                    httpCode = 200;
                    respBody = `<html><body>Hello ${urlPath[2]}, node.js!</body></html>`;
                }
                break;
            case 'post':
                // add me
                break;
        }
    } catch (err) {
        respBody = `Error: ${err.message}`;
        httpCode = 500;
    }

    res.writeHead(httpCode, { 'Content-Type': contentType });
    res.write(respBody);
    res.end();
});

server.listen(PORT);

console.log(`Server started, port ${PORT}`);

调试,-v可以输出完整的接受到的包信息

curl -v http://127.0.0.1:3000/greet

7、一个神奇的报错
[nodemon] app crashed - waiting for file changes before starting...

其实是因为我的js里有个语法错误,一个括号没有闭合而已,和文件有没有改变没有关系

8、使用url包,自动解析url,更易于应对更加复杂的url

'use strict';
const http = require('http');

const PORT = 3000;

// const qs = require('querystring');
const url = require('url');

const server = http.createServer((req, res) => {
    let respBody = '';
    let contentType = 'text/html';
    let httpCode = 404;

    console.error(req.url)
    const reqUrl = url.parse(req.url, true);
    console.error(reqUrl)
    const urlPath = reqUrl.pathname.split('/');

    console.error(urlPath)

    if (urlPath.length === 2 && urlPath[1] === 'greet') {
        httpCode = 200;
        respBody = reqUrl.query.name ? `<html><body>Hello ${reqUrl.query.name}, node.js!</body></html>` :
        '<html><body>Hello, node.js!</body></html>';
    } else { }

    res.writeHead(httpCode, { 'Content-Type': contentType });
    res.write(respBody);
    res.end();
})

server.listen(PORT);
// 这行代码将被执行,因为js的异步机制,而在python中则不会执行到这
console.log(`Server started, port ${PORT}`);

9、express中间件

我猜测express是基于html包做了封装,或者实现了类似html包的功能,也能提供服务,监听端口,并进行路由,以及其他更强大的功能

安装express,因为不需要在terminal使用,因此不用加-g(globally),直接locally安装即可

npm install express

index.js文件

'use strict';
const express = require('express');
const app = express();

app.get('/greet', (req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write('<html><body>Hello, express!</body></html>');
    res.end();
});

app.listen(3000);

可见这里我们没有处理除了/greet以外的路径,但express会自动处理此问题,返回404,不会像html包那样需要自己处理

10、静态内容

创建public文件夹,放入index.html

<html>
 <body>
 <p>
 Hello, static page!
 </p>
 </body>
</html>

在index.js添加app.use那一句

'use strict';
const express = require('express');
const app = express();

app.use(express.static('public'));

app.get('/greet', (req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write('<html><body>Hello, express!</body></html>');
    res.end();
});

app.listen(3000);

此时访问直接访问http://127.0.0.1:3000时就不会报错,会自动返回该index.html

添加图片:在public下创建image文件夹,并添加一张图

在index.html中使用

<html>
 <body>
 <p>
 Hello, static page!
 </p>
 <div id="mascot">
 <a href="https://www.google.com/search?q=silly+cats">
 <img src="/image/deeplearning.jpg" />
 </a>
 </div>
 </body>
</html>

11、url传参

'use strict';
const express = require('express');
const app = express();

app.use(express.static('public'));

app.get('/greet', (req, res) => {
 res.writeHead(200, { 'Content-Type': 'text/html' });
 res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
 res.end();
});
app.get('/greet/:firstName/:lastName', (req, res) => {
 res.writeHead(200, { 'Content-Type': 'text/html' });
 res.write(`<html><body>Hello, ${req.params.firstName} ${req.params.lastName}</body></html>`);
 res.end();
});

app.listen(3000);

比如

http://127.0.0.1:3000/greet?first_name=1&last_name=xixi
得到Hello, 1 xixi

http://127.0.0.1:3000/greet/xixi/haha
得到Hello, xixi haha

12、npm install express-list-endpoints

查看定义的路由

index.js

'use strict';
const express = require('express');
const listEndpoints = require('express-list-endpoints');

const app = express();

console.log(listEndpoints(app));

app.use(express.static('public'));

app.get('/greet', (req, res) => {
 res.writeHead(200, { 'Content-Type': 'text/html' });
 res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
 res.end();
});
app.get('/greet/:firstName/:lastName', (req, res) => {
 res.writeHead(200, { 'Content-Type': 'text/html' });
 res.write(`<html><body>Hello, ${req.params.firstName} ${req.params.lastName}</body></html>`);
 res.end();
});

console.log(listEndpoints(app));

app.listen(3000);

得到结果,可见定义后输出了我们定义的路由

[ { path: '', methods: [], middlewares: [] } ]
[
  { path: '/greet', methods: [ 'GET' ], middlewares: [ 'anonymous' ] },
  {
    path: '/greet/:firstName/:lastName',
    methods: [ 'GET' ],
    middlewares: [ 'anonymous' ]
  }
]

13、uuid

一个debug小技巧,用nodemon启动index.js如果有bug很难看出问题,而换成node启动则会直接报错提示,便于调试

安装uuid

npm install uuid

index.js文件

'use strict';

const uuid = require('uuid');
const express = require('express');
const app = express();

app.use(express.static('public'));


app.get('/greet', (req, res) => {
    req.my_data = {
        start_at: new Date(),
        request_id: uuid.v4()
    };

    console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)

    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
    res.end();
});

app.listen(3000);

也可以把greet中的语句分开写,但需要加入next(),不然不会执行下一个greet,而是直接结束该函数的执行

'use strict';

const uuid = require('uuid');
const express = require('express');
const app = express();

app.use(express.static('public'));

app.get('/greet', (req, res, next) => {
    req.my_data = {
        start_at: new Date(),
        request_id: uuid.v4()
    };

    next();
});

app.get('/greet', (req, res, next) => {

    console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
    next();
});

app.get('/greet', (req, res, next) => {

    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
    res.end();

});

app.listen(3000);

也可以定义到一个get里面,但next还是不能省

'use strict';

const uuid = require('uuid');
const express = require('express');
const app = express();

app.use(express.static('public'));

app.get('/greet',
    (req, res, next) => {
        req.my_data = {
            start_at: new Date(),
            request_id: uuid.v4()
        };
        next();
    },
    (req, res, next) => {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
        res.end();
        next();
    },
    (req, res, next) => {
        console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
        next();
    }
);

app.listen(3000);

还可以单独写成函数,便于阅读

'use strict';

const uuid = require('uuid');
const express = require('express');
const app = express();

app.use(express.static('public'));

function preResponse(req, res, next) {
    req.my_data = {
    start_at: new Date(),
    request_id: uuid.v4()
    };
    next();
}

function postResponse(req, res, next) {
    console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
    next(); 
}

app.get('/greet',
    preResponse,
    (req, res, next) => {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
        res.end();
        next();
    },
    postResponse
);

app.listen(3000);

将preResponse和postResponse绑定到所有的请求上(但访问http://127.0.0.1:3000不会触发这两个函数)

'use strict';

const uuid = require('uuid');
const express = require('express');
const app = express();

app.use(express.static('public'));

function preResponse(req, res, next) {
    req.my_data = {
    start_at: new Date(),
    request_id: uuid.v4()
    };
    next();
}

function postResponse(req, res, next) {
    console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
    next(); 
}

app.use(preResponse);

app.get('/greet', (req, res, next) => {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
    res.end();
    next();
});

app.get('/hello', (req, res, next) => {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write(`<html><body>Hello World</body></html>`);
    res.end();
    next();
});

app.use(postResponse);

app.listen(3000);

这样访问http://127.0.0.1:3000/hello或者http://127.0.0.1:3000/greet都会触发preResponse和postResponse

14、GET FORM

index.html文件

<html>
    <body>
        <p>
            Hello, static page!
        </p>
        <div id="mascot">
            <a href="https://www.google.com/search?q=silly+cats">
                <img src="/image/deeplearning.jpg" />
            </a>
        </div>
        <div id="form-container">
            <form ACTION="/greet" METHOD="GET">
                <label for="first_name">First name:</label><input type="text" name="first_name" id="first_name" />
                <br />
                <label for="last_name">Last name:</label><input type="text" name="last_name" id="last_name" />
                <br />
                <input type="submit">
            </form>
        </div>
    </body>
</html>

js文件

'use strict';

const uuid = require('uuid');
const express = require('express');
const app = express();

app.use(express.static('public'));

function preResponse(req, res, next) {
    req.my_data = {
    start_at: new Date(),
    request_id: uuid.v4()
    };
    next();
}

function postResponse(req, res, next) {
    console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
    next(); 
}

app.use(preResponse);

app.get('/greet', (req, res, next) => {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
    res.end();
    next();
});

app.get('/hello', (req, res, next) => {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write(`<html><body>Hello World</body></html>`);
    res.end();
    next();
});

app.use(postResponse);

app.listen(3000);

这样进入http://127.0.0.1:3000/时,会自动进入public文件夹下的index.html,然后会看到表格,输入姓和名,点击submit,会自动请求greet并将填写的内容作为参数传递,server收到后将得到的参数写到一个简单的html源码内并返回,此时浏览器客户端页面自动跳转,将看到自己填写的姓和名

GET 不够安全,内容全在url上(http://127.0.0.1:3000/greet?first_name=LUJIA&last_name=ZHONG),也很难传送文件

15、POST FORM

使用POST方法,表格内容都在HTTP的request body里;因此需要单独再实现一个app.post,而且参数无法从req.query拿到了

新的index.html

<html>
    <body>
        <p>
            Hello, static page!
        </p>
        <div class="form-container">
            <h4>GET</h4>
            <form ACTION="/greet" METHOD="GET">
                <label for="first_name">First name:</label><input type="text" name="first_name" id="first_name" />
                <br />
                <label for="last_name">Last name:</label><input type="text" name="last_name" id="last_name" />
                <br />
                <input type="submit" value="Say Hello">
            </form>
        </div>
        <div class="form-container">
            <h4>POST</h4>
            <form ACTION="/greet" METHOD="POST">
                <label for="first_name">First name:</label><input type="text" name="first_name" id="first_name" />
                <br />
                <label for="last_name">Last name:</label><input type="text" name="last_name" id="last_name" />
                <br />
                <input type="submit" value="Say Hello">
            </form>
        </div>
        <div id="mascot">
            <a href="https://www.google.com/search?q=silly+cats">
                <img src="/image/deeplearning.jpg" />
            </a>
        </div>
    </body>
</html>

可以用如下代码片段查看body(直接加入到之前的代码里即可

app.post('/greet', (req, res, next) => {
    let body = '';
    req.on('data', chunk => {
        body += chunk;
    });
    req.on('end', () => {
        console.error(body);
        next();
    });
});

可以在命令行界面得到first_name=Lujia&last_name=Zhong的输出

继续用如下代码进行调试,这里我们用了内置的querystring包

const qs = require('querystring');

app.post('/greet', (req, res, next) => {
    let body = '';
    req.on('data', chunk => {
        body += chunk;
    });
    req.on('end', () => {
        const query = qs.parse(body);
        console.error(query);
        next();
    });
});

会得到 [Object: null prototype] { first_name: ‘Lujia’, last_name: ‘Zhong’ }

当然以上的调试代码会导致浏览器出现Cannot POST /greet,毕竟我们并没有写如何回应浏览器的代码

完整index.js代码,此时使用POST表格正常跳转,且可见参数并不在url上

'use strict';

const uuid = require('uuid');
const express = require('express');
const app = express();

app.use(express.static('public'));

function preResponse(req, res, next) {
    req.my_data = {
    start_at: new Date(),
    request_id: uuid.v4()
    };
    next();
}

function postResponse(req, res, next) {
    console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
    next(); 
}

app.use(preResponse);

app.get('/greet', (req, res, next) => {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
    res.end();
    next();
});

app.get('/hello', (req, res, next) => {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write(`<html><body>Hello World</body></html>`);
    res.end();
    next();
});

const qs = require('querystring');

app.post('/greet', (req, res, next) => {
    let body = '';
    req.on('data', chunk => {
        body += chunk;
    });
    req.on('end', () => {
        const query = qs.parse(body);
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write(`<html><body>Hello, ${query.first_name} ${query.last_name}</body></html>`);
        res.end();
        next();
    });
});

app.use(postResponse);

app.listen(3000);

16、body-parser解析body,而不用querystring

下载body-parser

npm install body-parser

index.js,唯一改的就是引入库和POST那几行

'use strict';

const uuid = require('uuid');
const express = require('express');
const app = express();

app.use(express.static('public'));

function preResponse(req, res, next) {
    req.my_data = {
    start_at: new Date(),
    request_id: uuid.v4()
    };
    next();
}

function postResponse(req, res, next) {
    console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
    next(); 
}

app.use(preResponse);

app.get('/greet', (req, res, next) => {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
    res.end();
    next();
});

app.get('/hello', (req, res, next) => {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write(`<html><body>Hello World</body></html>`);
    res.end();
    next();
});

const BodyParser = require('body-parser');
const bodyParser = BodyParser.urlencoded({ extended: false });

app.post('/greet', bodyParser, (req, res, next) => {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write(`<html><body>Hello, ${req.body.first_name} ${req.body.last_name}</body></html>`);
    res.end();
    next();
});

app.use(postResponse);

app.listen(3000);

Html文件不变,可见bodyParser的传入使得能够直接从req访问到body以及其中的参数

17、FORM上传文件

index.html

<html>
    <body>
        <p>
            Hello, static page!
        </p>
    <div class="form-container">
        <h4>POST</h4>
        <form ACTION="/greet" METHOD="POST" ENCTYPE="multipart/form-data">
            <label for="first_name">First name:</label><input type="text" name="first_name" id="first_name" />
            <br />
            <label for="last_name">Last name:</label><input type="text" name="last_name" id="last_name" />
            <br />
            <label for="avatar">Avatar:</label><input type="file" name="avatar_pic" id="avatar" />
            <br />
            <input type="submit" value="Say Hello">
        </form>
    </div>
    </body>
</html>

其中ENCTYPE被指定成了"multipart/form-data",因为一般情况下,如果POST上传的东西有二进制的数据,比如这个图片,而不只是名字等字符串,或者当上传文件特别大时,我们选择使用multipart/form-data;

其他情况则用application/x-www-form-urlencoded,这个是默认值,不用自己指定

index.js,调试用

'use strict';

const uuid = require('uuid');
const express = require('express');
const app = express();

app.use(express.static('public'));

function preResponse(req, res, next) {
    req.my_data = {
    start_at: new Date(),
    request_id: uuid.v4()
    };
    next();
}

function postResponse(req, res, next) {
    console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
    next(); 
}

app.use(preResponse);

app.get('/greet', (req, res, next) => {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
    res.end();
    next();
});

app.post('/greet', (req, res, next) => {
    let body = '';
    req.on('data', chunk => {
        body += chunk;
    });
    req.on('end', () => {
        console.log(body);
        res.end('DEVELOPING'); 
        next();
    });
});

app.use(postResponse);

app.listen(3000);

浏览器正常收到返回的DEVELOPING,命令行查看输出的body

------WebKitFormBoundaryRBmt4TEHt22fuEXP
Content-Disposition: form-data; name="first_name"

Lujia
------WebKitFormBoundaryRBmt4TEHt22fuEXP
Content-Disposition: form-data; name="last_name"

Zhong
------WebKitFormBoundaryRBmt4TEHt22fuEXP
Content-Disposition: form-data; name="avatar_pic"; filename="948-bag-of-tokens.cpp"
Content-Type: text/plain

///
// a very clear solution

// Time Complexity: O(nlogn)
// Space Complexity: O(logn)
///
class Solution {
public:
    int bagOfTokensScore(vector<int>& tokens, int power) {
        sort(tokens.begin(), tokens.end());
        int res = 0, scores = 0, i = 0, j = tokens.size() - 1;
        while (i <= j) {
            if (power >= tokens[i]) {
                power -= tokens[i++];
                res = max(res, ++scores);
            } else if (scores > 0) {
                scores--;
                power += tokens[j--];
            } else {
                break;
            }
        }
        return res;
    }
};
------WebKitFormBoundaryRBmt4TEHt22fuEXP--

Request complete -- path:/greet, status:200, id:35055e0d-8cc9-4ff2-8af2-9dedc25f4486

可以看到能够接收到文件的内容

继续调试,使用multer,并用其来保存接收到的文件

npm install multer

js代码

'use strict';

const uuid = require('uuid');
const express = require('express');
const app = express();

app.use(express.static('public'));

function preResponse(req, res, next) {
    req.my_data = {
    start_at: new Date(),
    request_id: uuid.v4()
    };
    next();
}

function postResponse(req, res, next) {
    console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
    next(); 
}

app.use(preResponse);

app.get('/greet', (req, res, next) => {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write(`<html><body>Hello, ${req.query.first_name} ${req.query.last_name}</body></html>`);
    res.end();
    next();
});

const UPLOAD_DIR = `${__dirname}/upload`;
const Multer = require('multer');
const upload = Multer({ dest: UPLOAD_DIR });

app.post('/greet', upload.single('avatar_pic'), (req, res, next) => {
    console.log(req.body, req.file);
    res.end('DEVELOPING'); 

    next();
});

app.use(postResponse);

app.listen(3000);

upload文件会被自动创建,并将上传的文件自动保存下来

输出如下,可以看到multer像querystring一样把参数解析出来了

[Object: null prototype] { first_name: 'Lujia', last_name: 'Zhong' } {
  fieldname: 'avatar_pic',
  originalname: '948-bag-of-tokens.cpp',
  encoding: '7bit',
  mimetype: 'text/plain',
  destination: '/usr/src/app/upload',
  filename: 'ac4c13e4064867d0e1dab05206943b39',
  path: '/usr/src/app/upload/ac4c13e4064867d0e1dab05206943b39',
  size: 847
}

18、客户端查看server本地文件

index.js

'use strict';

const uuid = require('uuid');
const express = require('express');
const app = express();

function preResponse(req, res, next) {
    req.my_data = {
    start_at: new Date(),
    request_id: uuid.v4()
    };
    next();
}

function postResponse(req, res, next) {
    console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
    next(); 
}

app.use(preResponse);

app.use(express.static('public'));
app.use('/assets', express.static('upload'));

app.use(postResponse);

app.listen(3000);

此时输入http://127.0.0.1:3000/assets/ac4c13e4064867d0e1dab05206943b39,最后这一串是某个文件的名字,随后该文件会被直接下载下来,而不是显示在浏览器

问题出在mime-type,浏览器使用mime-type来决定文件的处理方式,就像前面multer解析出来的:mimetype: ‘text/plain’,如果是图片则为image/jpeg

但因为之前multer保存下来的文件都没有类型后缀,所以我们需要改一改使用multer时的代码

index.js

'use strict';

const uuid = require('uuid');
const express = require('express');
const app = express();

app.use(express.static('public'));
app.use('/assets', express.static('upload'));

function preResponse(req, res, next) {
    req.my_data = {
    start_at: new Date(),
    request_id: uuid.v4()
    };
    next();
}

function postResponse(req, res, next) {
    console.log(`Request complete -- path:${req.path}, status:${res.statusCode}, id:${req.my_data.request_id}`)
    next(); 
}

app.use(preResponse);


const UPLOAD_DIR = `${__dirname}/upload`;
const Multer = require('multer');
const multerStorage = Multer.diskStorage({ 
    destination: UPLOAD_DIR,
    filename: function(req, file, cb) { 
        cb(null, uuid.v4() + '_' + file.originalname);
    }
});
const upload = Multer({ storage: multerStorage });

app.post('/greet', upload.single('avatar_pic'), (req, res, next) => {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write(`<html><body>Hello, ${req.body.first_name} ${req.body.last_name}<br /><img src="/assets/${req.file.filename}" /></body></html>`);
    res.end();
    next();
});

app.use(postResponse);

app.listen(3000);

index.html和之前一样

<html>
    <body>
        <p>
            Hello, static page!
        </p>
    <div class="form-container">
        <h4>POST</h4>
        <form ACTION="/greet" METHOD="POST" ENCTYPE="multipart/form-data">
            <label for="first_name">First name:</label><input type="text" name="first_name" id="first_name" />
            <br />
            <label for="last_name">Last name:</label><input type="text" name="last_name" id="last_name" />
            <br />
            <label for="avatar">Avatar:</label><input type="file" name="avatar_pic" id="avatar" />
            <br />
            <input type="submit" value="Say Hello">
        </form>
    </div>
    </body>
</html>

提交后浏览器结果
在这里插入图片描述
当然如果提交的是文本,<img>标签是不能显示的,但现在不论提交什么都能在server端找到文件,且文件后缀和提交的保持一致

可以通过assets直接访问:
(1)http://127.0.0.1:3000/assets/4b8ec1ee-3a37-4a3c-ba5f-655bd6387d1c_948-bag-of-tokens.cpp
在这里插入图片描述
(2)对图片http://127.0.0.1:3000/assets/b22832bf-f3bd-4b84-afcf-94ea00f11d3b_rick.jpg
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值