stream模式不能接受blob文件_一文带你层层解锁文件下载的奥秘

今天带来的主题是关于文件下载,通过本文带你领略文件下载的奥秘。本文会花费你较长的时间阅读,建议先收藏/点赞,然后查看你感兴趣的部分,平时也可以充当当做字典的效果来查询。

:) 不整不知道,一整,居然整出这么多情况,我只是想简单地做个页面仔。

前言

一图览全文,可以先看看大纲适不适合自己,如果你喜欢则继续往下阅读。

84b6767639b2e9c34d9a5534f4573e9f.png
一文了解文件下载

这一节呢,主要介绍一些前置知识,对一些基础知识的介绍,如果你觉得你是这个。⬇️⬇️⬇️,你可以跳过前言。

47725a267b5dde15b89f9b1a16fca2c1.png
和荣耀王者说你嘛呢?_荣耀_王者表情

前端的文件下载主要是通过 ,再加上 download属性,有了它们让我们的下载变得简单。

download此属性指示浏览器下载 URL 而不是导航到它,因此将提示用户将其保存为本地文件。如果属性有一个值,那么此值将在下载保存过程中作为预填充的文件名(如果用户需要,仍然可以更改文件名)。此属性对允许的值没有限制,但是 /\ 会被转换为下划线。大多数文件系统限制了文件名中的标点符号,故此,浏览器将相应地调整建议的文件名。( 摘自 https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/a)

注意:

  • 此属性仅适用于同源 URL。
  • 尽管 HTTP URL 需要位于同一源中,但是可以使用 blob: URL 和 data: URL ,以方便用户下载使用 JavaScript 生成的内容(例如使用在线绘图 Web 应用程序创建的照片)。

因此下载 url 主要有三种方式。(本文大部分以 blob 的方式进行演示)

9205a3706256acaf763f95d8e7171d85.png
image-20200830153314861

兼容性

可以看到它的兼容性也非常的可观(https://www.caniuse.com/#search=download)

75a024a0cc783b78e8f10b80a6f9e224.png
image-20200817232216749

为了避免很多代码的重复性,因为我抽离出了几个公共函数。(该部分可跳过,名字都比较可读,之后若是遇到不明白则可以在这里寻找)

export function downloadDirect(url) {
    const aTag = document.createElement('a');
    aTag.download = url.split('/').pop();
    aTag.href = url;
    aTag.click()
}
export function downloadByContent(content, filename, type) {
    const aTag = document.createElement('a');
    aTag.download = filename;
    const blob = new Blob([content], { type });
    const blobUrl = URL.createObjectURL(blob);
    aTag.href = blobUrl;
    aTag.click();
    URL.revokeObjectURL(blob);
}
export function downloadByDataURL(content, filename, type) {
    const aTag = document.createElement('a');
    aTag.download = filename;
    const dataUrl = `data:${type};base64,${window.btoa(unescape(encodeURIComponent(content)))}`;
    aTag.href = dataUrl;
    aTag.click();
}
export function downloadByBlob(blob, filename) {
    const aTag = document.createElement('a');
    aTag.download = filename;
    const blobUrl = URL.createObjectURL(blob);
    aTag.href = blobUrl;
    aTag.click();
    URL.revokeObjectURL(blob);
}
export function base64ToBlob(base64, type) {
    const byteCharacters = atob(base64);
    const byteNumbers = new Array(byteCharacters.length);
    for (let i = 0; i         byteNumbers[i] = byteCharacters.charCodeAt(i);
    }
    const buffer = Uint8Array.from(byteNumbers);
    const blob = new Blob([buffer], { type });
    return blob;
}

?????????????????????????????

(手动给不看以上内容的大佬画分割线)

??

所有示例Github地址:  https://github.com/hua1995116/node-demo/tree/master/file-download

在线Demo: https://qiufeng.blue/demo/file-download/index.html

d49338555c9bcba3731306f4b0ac09b0.png
前端文件下载

后端

本文后端所有示例均以 koa / 原生 js 实现。

后端返回文件流

这种情况非常简单,我们只需要直接将后端返回的文件流以新的窗口打开,即可直接下载了。

// 前端代码
<button id="oBtnDownload">点击下载button>
<script>
oBtnDownload.onclick = function(){window.open('http://localhost:8888/api/download?filename=1597375650384.jpg', '_blank')
}script>
// 后端代码
router.get('/api/download', async (ctx) => {
    const { filename } = ctx.query;
    const fStats = fs.statSync(path.join(__dirname, './static/', filename));
    ctx.set({
        'Content-Type': 'application/octet-stream',
        'Content-Disposition': `attachment; filename=${filename}`,
        'Content-Length': fStats.size
    });
    ctx.body = fs.readFileSync(path.join(__dirname, './static/', filename));
})

能够让浏览器自动下载文件,主要有两种情况:

一种为使用了Content-Disposition属性。

我们来看看该字段的描述。

在常规的HTTP应答中,Content-Disposition 响应头指示回复的内容该以何种形式展示,是以内联的形式(即网页或者页面的一部分),还是以附件的形式下载并保存到本地   --- 来源 MDN(https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Disposition)

再来看看它的语法

Content-Disposition: inline
Content-Disposition: attachment
Content-Disposition: attachment; filename="filename.jpg"

很简单,只要设置成最后一种形态我就能成功让文件从后端进行下载了。

另一种为浏览器无法识别的类型

例如输入 http://localhost:8888/static/demo.sh,浏览器无法识别该类型,就会自动下载。

不知道小伙伴们有没有遇到过这样的一个情况,我们输入一个正确的静态 js 地址,没有配置Content-Disposition,但是却会被意外的下载。

例如像以下的情况。

d5030ee09b887560138b86213cb9785c.gif
2020-08-30-17.01.52
3f40fc8047ca53bc5590b3968591dc20.png
006r3PQBjw1fav4dsikh6j308c0g5gm1

这很可能是由于你的 nginx 少了这一行配置.

include mime.types;

导致默认走了 application/octet-stream,浏览器无法识别就下载了文件。

后端返回静态站点地址

通过静态站点下载,这里要分为两种情况,一种为可能该服务自带静态目录,即为同源情况,第二种情况为适用了第三方静态存储平台,例如阿里云、腾讯云之类的进行托管,即非同源(当然也有些平台直接会返回)。

同源

同源情况下是非常简单,先上代码,直接调用一下函数就能轻松实现下载。

import {downloadDirect} from '../js/utils.js';
axios.get('http://localhost:8888/api/downloadUrl').then(res => {
        if(res.data.code === 0) {
            downloadDirect(res.data.data.url);
        }
})

非同源

我们也可以从 MDN 上看到,虽然 download 限制了非同源的情况,但是!!但是!!但是可以使用 blob: URL 和 data: URL ,因此我们只要将文件内容进行下载转化成 blob 就可以了。

整个过程如下

b1650532cfc896ca3ef706f8e832de6d.png
image-20200830174735143
<button id="oBtnDownload">点击下载button>
    <script type="module">import {downloadByBlob} from '../js/utils.js';function download(url) {
            axios({method: 'get',
                url,responseType: 'blob'
            }).then(res => {
                downloadByBlob(res.data, url.split('/').pop());
            }) 
        }
        oBtnDownload.onclick = function(){
           axios.get('http://localhost:8888/api/downloadUrl').then(res => {if(res.data.code === 0) {
                    download(res.data.data.url);
                }
            })
        }script>

现在非同源的也可以愉快地下载啦。

后端返回字符串(base64)

有时候我们也会遇到一些新手后端返回字符串的情况,这种情况很少见,但是来了我们也不慌,顺便可以向后端小哥秀一波操作,不管啥数据,咱都能给你下载下来。

ps: 前提是安全无污染的资源 :)  , 正经文章的招牌闪闪发光。

这种情况下,我需要模拟下后端小哥的骚操作,因此有后端代码。

68e349b60766bf419e14834758524db2.png
994b6f2egy1fgryfevtpvj208c08cmxd

核心过程

4107a535a2294cb183978042be70710e.png
image-20200830174752476
// node 端
router.get('/api/base64', async (ctx) => {
    const { filename } = ctx.query;
    const content = fs.readFileSync(path.join(__dirname, './static/', filename));
    const fStats = fs.statSync(path.join(__dirname, './static/', filename));
    console.log(fStats);
    ctx.body = {
        code: 0,
        data: {
            base64: content.toString('base64'),
            filename,
            type: mime.getType(filename)
        }
    }
})
// 前端
<button id="oBtnDownload">点击下载button>
<script type="module">import {base64ToBlob, downloadByBlob} from '../js/utils.js';function download({ base64, filename, type }) {const blob = base64ToBlob(blob, type);
    downloadByBlob(blob, filename);
}
oBtnDownload.onclick = function(){
    axios.get('http://localhost:8888/api/base64?filename=1597375650384.jpg').then(res => {if(res.data.code === 0) {
            download(res.data.data);
        }
    })
}script>

思路其实还是利用了我们上面说的 标签。但是在这个步骤前,多了一个步骤就是,需要将我们的 base64 字符串转化为二进制流,这个东西,在我的前一篇文件上传中也常常提到,毕竟文件就是以二进制流的形式存在。不过也很简单,js 拥有内置函数 atob。极大地提高了我们转换的效率。

纯前端

上面介绍借助后端来完成文件下载的相关方法,接下来我们来介绍介绍纯前端来完成文件下载的一些方法。

方法一:  blob: URL

e197f52221020d3d7e7613459e878f1c.png
image-20200831230800538

方法二: data: URL

a9647f8ad42ca769555008548056d428.png
image-20200831230810963

由于 data:URL 会有长度的限制,因此下面的所有例子都会采用 blob 的方式来进行演示。

json/text

下载text和json非常的简单,可以直接构造一个 Blob。

Blob(blobParts[, options])
返回一个新创建的 Blob 对象,其内容由参数中给定的数组串联组成。
// html
<textarea name="" id="text" cols="30" rows="10">textarea>
<button id="textBtn">下载文本button>
<p>p>
<textarea name="" id="json" cols="30" rows="10" disabled>
{
    "name": "秋风的笔记"
}
textarea>
<button id="jsonBtn">下载JSONbutton>
//js
import {downloadByContent, downloadByDataURL} from '../js/utils.js';
textBtn.onclick = () => {
        const value = text.value;
        downloadByContent(value, 'hello.txt', 'text/plain');
    // downloadByDataURL(value, 'hello.txt', 'text/plain');
}
jsonBtn.onclick = () => {
        const value = json.value;
        downloadByContent(value, 'hello.json', 'application/json');
     // downloadByDataURL(value, 'hello.json', 'application/json');
}

效果图

512256109ac3c108e4ec6bd9fffa6768.gif
2020-08-30-17.53.32

注释代码为 data:URL 的展示部分,由于是第一个例子,因此我讲展示代码,后面都省略了,但是你也可以通过调用 downloadByDataURL 方法,找不到该方法的定义请滑到文章开头哦~

excel

excel 可以说是我们部分前端打交道很深的一个场景,什么数据中台,天天需要导出各种报表。以前都是前端请求后端,来获取一个 excel 文件地址。现在让我们来展示下纯前端是如何实现下载excel。

简单excel

表格长这个模样,比较简陋的形式

9ea31e4ec89d6c4991f22812947e0e97.png
image-20200829170347728
const template = '
            +'xmlns:x="urn:schemas-microsoft-com:office:excel" '
            +'xmlns="http://www.w3.org/TR/REC-html40">'
            +''
            +''
            +'
{table}'
            +'';const context = template.replace('{table}', document.getElementById('excel').innerHTML);
    downloadByContent(context, 'qiufengblue.xls', 'application/vnd.ms-excel');

但是编写并不复杂,依旧是和我们之前一样,通过构造出 excel 的格式,转化成 blob 来进行下载。

最终导出的效果

56a7ed63282bd4e0b870a42e159628b0.png
image-20200829170625763

element-ui 导出表格

没错,这个就是 element-ui 官方table 的例子。

de02f6dfb168ac9856c690db011a1095.png
image-20200829170543891

导出效果如下,可以说非常完美。

3ec8b70076a56095fbf4417021f90934.png
image-20200829170912128

这里我们用到了一个插件 https://github.com/SheetJS/sheetjs

使用起来非常简单。

<template>
      <el-table id="ele" border :data="tableData" style="width: 100%">
        <el-table-column prop="date" label="日期" width="180">
        el-table-column>
        <el-table-column prop="name" label="姓名" width="180">
        el-table-column>
        <el-table-column prop="address" label="地址">
        el-table-column>
      el-table>
      <button @click="exportExcel">导出excelbutton>
template>
<script>
...
methods: {
  exportExcel() {let wb = XLSX.utils.table_to_book(document.getElementById('ele'));
     XLSX.writeFile(wb, 'qiufeng.blue.xlsx');
 }
}
...script>

74607de599e5ee51ab63f173dc9399fe.png
完美表情

word

讲完了 excel我们再来讲讲 word 这可是 office 三剑客另外一大利器。这里我们依旧是利用上述的 blob 的方法进行下载。

简单示例

5b585408956c5772176b2c7c5064adf2.gif
2020-08-29-20.13.25

代码展示

exportWord.onclick = () => {
    const template = '
            +'xmlns:x="urn:schemas-microsoft-com:office:word" '
            +'xmlns="http://www.w3.org/TR/REC-html40">'
            +''
            +''
            +'{table}'
            +'';const context = template.replace('{table}', document.getElementById('word').innerHTML);
    downloadByContent(context, 'qiufeng.blue.doc', 'application/msword');
}

效果展示

002cf410e18e5e95b979df904a24ba9b.png
image-20200830164208184

使用 docx.js插件

如果你想有更高级的用法,可以使用 docx.js这个库。当然用上述方法也是可以高级定制的。

代码

<button type="button" onclick="generate()">下载wordbutton>

    <script>async function generate() {const res = await axios({method: 'get',url: 'http://localhost:8888/static/1597375650384.jpg',responseType: 'blob'
            })const doc = new docx.Document();const image1 = docx.Media.addImage(doc, res.data, 300, 400)
            doc.addSection({properties: {},children: [new docx.Paragraph({children: [new docx.TextRun("欢迎关注[秋风的笔记]公众号").break(),new docx.TextRun("").break(),new docx.TextRun("定期发送优质文章").break(),new docx.TextRun("").break(),new docx.TextRun("美团点评2020校招-内推").break(),
                        ],
                    }),new docx.Paragraph(image1),
                ],
            }); 
            docx.Packer.toBlob(doc).then(blob => {console.log(blob);
                saveAs(blob, "qiufeng.blue.docx");console.log("Document created successfully");
            });
        }script>

效果(没有打广告...随便找了张图,强行不承认系列)

9b68fbff97aca91a284620b946123877.png
9150e4e5ly1fl8qavz6quj20hs0hsjvl
9ad8dde838d256a643fb992e631e9d90.gif
2020-08-30-18.32.09

zip下载

前端压缩还是非常有用的,在一定的场景下,可以节省流量。而这个场景比较使用于,例如前端打包图片下载、前端打包下载图标。

一开始我以为我 https://tinypng.com/ 就是用了这个,结果我发现我错了...仔细一想,因为它压缩好的图片是存在后端的,如果使用前端打包的话,反而要去请求所有压缩的图片从而来获取图片流。如果用后端压缩话,可以有效节省流量。嗯。。。失败例子告终。

后来又以为https://www.iconfont.cn/打包下载图标的时候,使用了这个方案....发现....我又错了...但是我们分析一下.

c53c2dc5d3d6604c423e9b8f9af93177.png
image-20200829204540440

它官网都是 svg 渲染的图标,对于 svg 下载的时候,完全可以使用前端打包下载。但是,它还支持 font 以及 jpg 格式,所以为了统一,采用了后端下载,能够理解。那我们就来实现这个它未完成的功能,当然我们还需要用到一个插件,就是 jszip。

这里我从以上找了两个 svg 的图标。

77449a1c3cb414ad42dfac31c68cfd24.png
image-20200829204937044

实现代码

download.onclick = () => {
        const zip = new JSZip();
        const svgList = [{
            id: 'demo1',
        }, {
            id: 'demo2',
        }]
        svgList.map(item => {
            zip.file(item.id + '.svg', document.getElementById(item.id).outerHTML);
        })
        zip.generateAsync({ 
            type: 'blob'
        }).then(function(content) {
            // 下载的文件名
            var filename = 'svg' + '.zip';
            // 创建隐藏的可下载链接
            var eleLink = document.createElement('a');
            eleLink.download = filename;
            // 下载内容转变成blob地址
            eleLink.href = URL.createObjectURL(content);
            // 触发点击
            eleLink.click();
            // 然后移除
        });
    }
b4bc389eedf99dc55871834e31b4504c.gif
2020-08-29-20.52.42

查看文件夹目录,已经将 SVG 打包下载完毕。

a7724588fda472fcf06253871dd9d849.png
image-20200829205329532

浏览器文件系统(实验性)

0ed1eb79c8d0b12c700c936d29ed105f.png
image-20200817234129788

在我电脑上都有这么一个浏览器,用来学习和调试 chrome 的最新新特性, 如果你的电脑没有,建议你安装一个。

玩这个特性需要打开 chrome 的实验特性 chrome://flags => #native-file-system-api => enable, 因为实验特性都会伴随一些安全或者影响原本的渲染的行为,因此我再次强烈建议,下载一个金丝雀版本的 chrome 来进行玩耍。

<textarea name="" id="textarea" cols="30" rows="10">textarea>
<p><button id="btn">下载button>p>
<script>
    btn.onclick = async () => {const handler = await window.chooseFileSystemEntries({type: 'save-file',accepts: [{description: 'Text file',extensions: ['txt'],mimeTypes: ['text/plain'],
            }],
        });const writer = await handler.createWritable();await writer.write(textarea.value);await writer.close();
    }script>

实现起来非常简单。却飞一般的感觉。

28c9c644fcdb660080dc6222aa94708f.gif
2020-08-18-00.13.29

其他场景

H5文件下载

一般在 h5 下载比较多的是 pdf 或者是 apk 的下载。

Android

在安卓浏览器中,浏览器直接下载文件。

ios

由于ios的限制,无法进行下载,因此,可以使用复制 url ,来代替下载。

import {downloadDirect} from '../js/utils.js';
const btn = document.querySelector('#download-ios');
if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
    const clipboard = new ClipboardJS(btn);
    clipboard.on('success', function () {
        alert('已复制链接,打开浏览器粘贴链接下载');
    });
    clipboard.on('error', function (e) {
        alert('系统版本过低,复制链接失败');
    });
} else {
    btn.onclick = () => {
        downloadDirect(btn.dataset.clipboardText)
    }
}

更多

对于 apk 等下载包可以使用这个包(本人暂时没有试验,接触不多,回头熟悉了再回来补充。)

https://github.com/jawidx/web-launch-app

450843b2590ed2475cb5dcb1ce5dab9c.png
image-20200830145258473

大文件的分片下载

最近在开发媒体流相关的工作的时候,发现在加载 mp4 文件的时候,发现了一个比较有意思的现象,视频流并不需要将整个 mp4 下载完才进行播放,并且伴随了很多状态码为 206 的请求,乍一看有点像流媒体(HLS等)的韵味。

b360277a6f0389bee33c8abc8cb24458.gif
2020-08-29-21.31.29

觉得这个现象非常的有意思,他能够分片地加载资源,这对于体验或者是流量的节省都是非常大的帮助。最终发现它带了一个名为 Range 的头。我们来看看 MDN 的解释。

The Range 是一个请求首部,告知服务器返回文件的哪一部分。在一个 Range 首部中,可以一次性请求多个部分,服务器会以 multipart 文件的形式将其返回。如果服务器返回的是范围响应,需要使用 206 Partial Content 状态码。 摘自 MDN

语法

Range: =-
Range: =-
Range: =-, -
Range: =-, -, -

Node实现

既然我们知道了它的原理,就来自己实现一下。

router.get('/api/rangeFile', async(ctx) => {
    const { filename } = ctx.query;
    const { size } = fs.statSync(path.join(__dirname, './static/', filename));
    const range = ctx.headers['range'];
    if (!range) {
        ctx.set('Accept-Ranges', 'bytes');
        ctx.body = fs.readFileSync(path.join(__dirname, './static/', filename));
        return;
    }
    const { start, end } = getRange(range);
    if (start >= size || end >= size) {
        ctx.response.status = 416;
        ctx.set('Content-Range', `bytes */${size}`);
        ctx.body = '';
        return;
    }
    ctx.response.status = 206;
    ctx.set('Accept-Ranges', 'bytes');
    ctx.set('Content-Range', `bytes ${start}-${end ? end : size - 1}/${size}`);
    ctx.body = fs.createReadStream(path.join(__dirname, './static/', filename), { start, end });
})

Nginx实现

发现 nginx 不需要写任何代码就默认支持了 range 头,想着我一定知道它到底是支持,还是加入了什么模块,或者是我默认开启了什么配置,找了半天没有找到什么额外的配置。

b1181de8f49b153325a52c390e9dffb5.png
3630px-Nginx_logo-1

正当我准备放弃的时候,灵光一现,去看看源码吧,说不定会有发现,去查了 nginx 源码相关的内容,用了惯用的反推方式,才发现原来是max_ranges这个字段。

https://github.com/nginx/nginx/blob/release-1.13.6/src/http/modules/ngx_http_range_filter_module.c#L166

这也怪我一开始文档阅读不够仔细,浪费了大量的时间。

:) 其实我对 nginx 源码也不熟悉,这里可以用个小技巧,直接在源码库 搜索 206 然后 发现了一个宏命令

#define NGX_HTTP_PARTIAL_CONTENT           206

然后顺藤摸瓜,直接找到这个宏命令NGX_HTTP_PARTIAL_CONTENT用到的地方,这样一步一步就慢慢能找到我们想要的。

默认 nginx 是自动开启 range 头的, 如果不需要配置,则配置 max_range: 0;

Nginx 配置文档 http://nginx.org/en/docs/http/ngx_http_core_module.html#max_ranges

总结

我们可以来总结一下,其实全文主要讲了(xbb)两个核心的知识,一个是 blob 一个a 标签,另外还要注意对于大文件,服务器的优化策略,可以通过 Range 来分片加载。

2768959e8fa79345720b787364a0edfd.png
image-20200830181216353

参考资料

https://github.com/dolanmiu/docx

https://github.com/SheetJS/sheetjs

https://juejin.im/post/6844903763359039501

最后

如果我的文章有帮助到你,希望你也能帮助我,欢迎关注我的微信公众号 前端有的玩,回复好友 二次,可加个人微信并且加入交流群,前端有的玩 将一直陪伴你的左右。

52df272d8443b7b58c164865831631b4.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/349517.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

# 管道已结束_县城这条路启用自来水新管道,看看是否在你家附近...

连日来&#xff0c;县自来水公司的工作人员顶着高温抓紧作业&#xff0c;目前&#xff0c;县城范堤路自来水管道改造工程已过半&#xff0c;预计本月中旬完工。施工现场1目前&#xff0c;盐垣路至掘中路的新管道正在进行对接&#xff0c;为了确保启用新管道后的用水安全&#x…

python 中的eval与exec

eval类似exec,是使用python编译器运行表达式和语句两者区别在于:eval是编译表达式并返回值(如: eval("hello*2") 结果是 hellohello)exec则是运行一部分代码,并且不像eval那样返回结果,exec的返回值永远是None,且exec可运行多行代码(如: exec("l[1,2,3]\nfor i …

启动时指定需要绑定的网卡_为什么小型汽油机在启动时需要拉风门,而汽车却不用?...

很多人都有骑小型摩托车的经验&#xff0c;在启动摩托车时&#xff0c;一般要把“合风”拉上&#xff0c;否则摩托车启动就会很困难。特别是在天气比较寒冷时&#xff0c;如果不拉上合风&#xff0c;摩托车很难启动。但是在汽车上却没有“合风”这个装置&#xff0c;我们在启动…

java 栈和队列实现迷宫代码_Python 实现数据结构中的的栈,队列

栈栈(stack)又名堆栈&#xff0c;它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶&#xff0c;相对地&#xff0c;把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈&#xff0c;它是把新元素放到栈顶元素的上面&#xff…

管理任务执行-如何排任务优先级

背景 工程师出生的管理者擅长做任务管理&#xff0c;凡是明确答应过的事情&#xff0c;一般会如数兑现。 研究的含义 研究模块核心管理规划把事情做对团队建设理顺做事的主体任务管理把事情做出来&#xff0c;产出实实在在的业绩和成果做事【任务管理】是非常重要的管理内容 对…

maven项目 ant_将大型项目从Ant迁移到Maven

maven项目 ant事实是我们处在艰难时期。 我们花了将近三个月的时间将构建机制从Ant迁移到Maven 。 如果您打算在大型项目中进行同样的安排&#xff0c;那是您必须安排的最短时间。 我们仍在努力解决这种迁移带来的一些附带影响&#xff0c;但幸运的是&#xff0c;它们并不是那么…

缩影和掠影_普查员的“酸苦甜” 社区人口普查工作掠影

普查员的“酸苦甜”--社区人口普查工作掠影/十年一度的人口普查即将收官自十月中旬开展摸底工作以来长安路三社区的普查员们伴着晨曦出发&#xff0c;伴着星光回家&#xff0c;走街串巷、爬楼入户。他们中有社区干部、有社区工作者、还有热心的志愿者们&#xff0c;大家都绷着一…

八数码深度优先搜索_深度优先搜索和广度优先搜索

深度优先搜索和广度优先搜索关于搜索&遍历对于搜索来说&#xff0c;我们绝大多数情况下处理的都是叫 “所谓的暴力搜索” &#xff0c;或者是说比较简单朴素的搜索&#xff0c;也就是说你在搜索的时候没有任何所谓的智能的情况在里面考虑&#xff0c;很多情况下它做的一件事…

element 日期控件 限制开始日期和结束日期

基于element ui中的时间日期控件的开始日期和结束日期的限制用picker-options属性&#xff0c; 当前日期时间器特有的选项。 代码如下&#xff1a; <el-form-itemlabel"开始日期"label-width"90px"prop"startDate"><el-date-pickerv-mo…

JArchitect v2017发布!

我们很高兴地通知您&#xff0c; JArchitect v2017已发布 &#xff0c;现在可以下载 &#xff01; JArchitect是一个分析工具&#xff0c;确实可以让您深入研究代码的结构和质量 。 使用JArchitect&#xff0c;可以使用 代码指标 &#xff0c;使用可视化 图和树图 &#…

[C语言]程序练习(一)

你好&#xff0c;这里是争做图书馆扫地僧的小白。 个人主页&#xff1a;争做图书馆扫地僧的小白_-CSDN博客 目标&#xff1a;希望通过学习技术&#xff0c;期待着改变世界。 目录 前言 一、常量练习 &#xff08;一&#xff09;整型常量 &#xff08;二&#xff09;浮点型常…

如何在程序中不用加号实现加法_程序员那些事 | JavaScript基础(六)

Hello&#xff0c;程序员那些事又与大家见面了&#xff01;JS简易日历先给大家看一下效果&#xff1a;大家可能会觉得&#xff0c;唉~好像跟上节课讲的选项卡差不多&#xff0c;就是十二个按钮&#xff0c;改变this的class&#xff0c;同时下面放十二个div&#xff0c;每次显示…

用友UI层获取机构的方法

U层&#xff1a; UFIDA.U9.UI.PDHelper.PDContext.Current.OrgIDPDContext.Current.OrgRef.CodeColumn 转载于:https://www.cnblogs.com/Xanthus/p/11556836.html

求立方根_「每日一学」数学七上:立方根的知识要点

哈喽&#xff0c;大家好&#xff01;我们又见面了&#xff0c;欢迎继续关注【轩爸辅导】的【口袋数学】。日更【每日一学】【每日一练】&#xff0c;帮助孩子日积月累&#xff0c;考出好的成绩。配套辅导&#xff0c;哪里不会学哪里&#xff0c;哪里出错练哪里&#xff0c;帮助…

python声明编码格式_使用python将doc文件转为utf8编码格式的txt

最近花了点时间看了会doc的转换&#xff0c;果然官方文档最好用&#xff0c;代码如下&#xff1a; import os import sys import fnmatch import win32com.client PATH os.path.abspath(os.path.dirname(sys.argv[0])) doc_path PATH ‘/data/doc/’ txt_path PATH ‘/dat…

49反思

感谢自闭场 转载于:https://www.cnblogs.com/znsbc-13/p/11565800.html

mongodb适用于_适用于MongoDB和Mongometer的SpiderMonkey至V8

mongodb适用于通过10gen将MongoDB 2.3 / 2.4的默认JavaScript引擎从SpiderMonkey切换到V8&#xff0c;我想我将借此机会使用mongometer比较这些发行版的相对性能。 作为安全专家&#xff0c;我真的应该首先研究“ 其他身份验证功能” ……嘿。 我将记录比较过程中所采取的步骤…

地面指示标志_详解消防应急照明和疏散指示系统的施工及安装方法消防施工

详解消防应急照明和疏散指示系统的施工及安装方法消防施工消防应急照明与疏散指示系统在生活中随处可见&#xff0c;相信您并不陌生&#xff0c;它是为人员疏散、消防作业提供照明和疏散指示的系统&#xff0c;由各类消防应急灯具及相关装置组成。自今年3月1日国家标准GB 51309…

python获取某文件路径_Python获取当前文件路径

一. Python 获取当前文件路径方法 2. sys.path[0] 获取文件当前工作目录路径&#xff08;绝对路径&#xff09; sys.argv[0]|获得模块所在的路径&#xff08;由系统决定是否是全名&#xff09; 若显示调用python指令&#xff0c;如python demo.py&#xff0c;会得到绝对路径; 若…

Java认证值得吗?

在任何行业中获得证书总是有其利弊&#xff0c;但似乎在技术世界中&#xff0c;情况更加动荡。 对熟练的技术人员的需求如此&#xff0c;进入该行业的途径很多&#xff0c;包括学术认证方法和更自学的基于投资组合的方法。 但是Java认证是否值得追求以促进您的职业发展&#xf…