前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >项目之关于Summernote的图片处理和基于SpringMVC的文件上传(10)

项目之关于Summernote的图片处理和基于SpringMVC的文件上传(10)

作者头像
海拥
发布于 2021-08-23 08:18:18
发布于 2021-08-23 08:18:18
93900
代码可运行
举报
文章被收录于专栏:全栈技术全栈技术
运行总次数:0
代码可运行

45. 我的问答列表-前端页面

index.html页面,先找到整个列表区域的父级,为其添加id,以便于创建Vue对象:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!-- 约第185-->
<div class="container-fluid" id="questionsApp">

找到每个问题的显示区域,为这个区域的根级添加v-for以循环显示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!-- 约第193-->
<div v-for="question in questions" class="media bg-white m-2 p-3">

关于问题的状态:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<span class="badge badge-pill" v-bind:class="[question.statusClass]" v-text="question.statusText">已解决</span>

问题的标题:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<a class="text-dark" href="question/detail.html" v-text="question.title">eclipse 如何导入项目?</a>

问题的内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<div class="font-weight-light text-truncate text-wrap text-justify mb-2" style="height: 70px;" v-html="question.content">
	<p>
		eclipse 如何导入项目?
	</p>
</div>

问题的标签列表:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<a v-for="tag in question.tags" class="text-info badge badge-pill bg-light" href="tag/tag_question.html">
    <small v-text="tag.name">Java基础</small>
</a>

右下角的更多信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<small class="list-inline-item" v-text="question.userNickName">风继续吹</small>
<small class="list-inline-item"><span v-text="question.hits">12</span>浏览</small>
<small class="list-inline-item" v-text="question.createdTimeText">13分钟前</small>

显示图片:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<img v-bind:src="question.tagImage" class="ml-3 border img-fluid rounded" alt="" width="208" height="116">

添加了标签之后,在static下的**/js/question/文件夹下创建my.js**文件,先创建出Vue对象,并配置一些模拟的数据:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let questionsApp = new Vue({
    el: '#questionsApp',
    data: {
        questions: [
            {
                statusText: '已解决',
                statusClass: 'badge-success',
                title: '这是第1个问题',
                content: '很<b>严肃</b>的提出了第1个问题',
                tags: [
                    { id: 8, name: 'Java基础' },
                    { id: 12, name: 'Spring' },
                    { id: 15, name: 'SpringBoot' }
                ],
                userNickName: '天下第一',
                hits: '826',
                createdTimeText: '8小时之前',
                tagImage: '/img/tags/8.jpg'
            },
            {
                statusText: '未回复',
                statusClass: 'badge-warning',
                title: '这是第2个问题',
                content: '我也不告诉你是什么问题……',
                tags: [
                    { id: 10, name: 'MySQL' },
                    { id: 17, name: 'SpringSecurity' }
                ],
                userNickName: '天下第一',
                hits: '537',
                createdTimeText: '15小时之前',
                tagImage: '/img/tags/10.jpg'
            }
        ]
    }
});

然后,在index.html中调用以上文件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<script src="/js/question/my.js"></script>

完成后,先利用以上模拟的数据进行测试,也就是直接打开浏览器,观察运行效果与预期是否相符!

测试完成后,在my.js中,向服务器端发送请求获取真实的数据,并用于显示页面:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let questionsApp = new Vue({
    el: '#questionsApp',
    data: {
        questions: []
    },
    methods: {
        loadMyQuestions: function () {
            $.ajax({
                url: '/api/v1/questions/my',
                success: function (json) {
                    // json.data.list
                    let data = json.data;
                    let questions = data.list;
                    let statusTexts = ['未回复', '未解决', '已解决'];
                    let statusClasses = ['badge-warning', 'badge-info', 'badge-success'];
                    for (let i = 0; i < questions.length; i++) {
                        questions[i].statusText = statusTexts[questions[i].status];
                        questions[i].statusClass = statusClasses[questions[i].status];
                        questions[i].tagImage = '/img/tags/' + questions[i].tags[0].id + '.jpg';

                        questions[i].createdTimeText = "未知时间";
                    }
                    questionsApp.questions = questions;
                }
            });
        }
    },
    created: function () {
        this.loadMyQuestions();
    }
});

关于将时间显示为“刚刚” / “xx分钟前”等格式的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let now = new Date().getTime();
let pastTime = (now - new Date(questions[i].createdTime).getTime()) / 1000;
let createdTimeText = "未知时间";
if (pastTime < 60) { // 不足1分钟
    createdTimeText = "刚刚";
} else if (pastTime < 60 * 60) { // 不足1小时
    createdTimeText = parseInt(pastTime / 60) + "分钟前";
} else if (pastTime < 60 * 60 * 24) {
    createdTimeText = parseInt(pastTime / 60 / 60) + "小时前";
} else {
    createdTimeText = parseInt(pastTime / 60 / 60 / 24) + "天前";
}

questions[i].createdTimeText = createdTimeText;

最后,关于显示分页,首先要使得以前加载数据的函数是支持页码参数的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
loadMyQuestions: function (page) {
    if (!page || page < 1) {
        page = 1;
    }
    $.ajax({
        url: '/api/v1/questions/my',
        data: 'page=' + page,
        // 省略后续代码
    });
}

关于分页的页面部分的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<nav aria-label="Page navigation example">
    <div class="pagination">
        <a class="page-item page-link" href="#"
           v-on:click.prevent="loadMyQuestions(pageInfo.prePage)">上一页</a>

        <a class="page-item page-link " href="#"
           v-for="i in pageInfo.navigatepageNums"
           v-on:click.prevent="loadMyQuestions(i)"
           v-bind:class="{ 'bg-primary text-light': i == pageInfo.pageNum }">
            <span v-text="i">1</span>
        </a>

        <a class="page-item page-link" href="#"
           v-on:click.prevent="loadMyQuestions(pageInfo.nextPage)">下一页</a>
    </div>
</nav>

my.js中,在属性中声明pageInfo

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
data: {
    questions: [],
    pageInfo: null
}

当获取数据后,添加:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
questionsApp.pageInfo = data;

至此,页面的显示已完成,关于my.js的完整代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let questionsApp = new Vue({
    el: '#questionsApp',
    data: {
        questions: [
            {
                statusText: '已解决',
                statusClass: 'badge-success',
                title: '这是第1个问题',
                content: '很<b>严肃</b>的提出了第1个问题',
                tags: [
                    { id: 8, name: 'Java基础' },
                    { id: 12, name: 'Spring' },
                    { id: 15, name: 'SpringBoot' }
                ],
                userNickName: '天下第一',
                hits: '826',
                createdTimeText: '8小时之前',
                tagImage: '/img/tags/8.jpg'
            },
            {
                statusText: '未回复',
                statusClass: 'badge-warning',
                title: '这是第2个问题',
                content: '我也不告诉你是什么问题……',
                tags: [
                    { id: 10, name: 'MySQL' },
                    { id: 17, name: 'SpringSecurity' }
                ],
                userNickName: '天下第一',
                hits: '537',
                createdTimeText: '15小时之前',
                tagImage: '/img/tags/10.jpg'
            }
        ],
        pageInfo: null
    },
    methods: {
        loadMyQuestions: function (page) {
            if (!page || page < 1) {
                page = 1;
            }
            $.ajax({
                url: '/api/v1/questions/my',
                data: 'page=' + page,
                success: function (json) {
                    // json.data.list
                    let data = json.data;
                    let questions = data.list;
                    let statusTexts = ['未回复', '未解决', '已解决'];
                    let statusClasses = ['badge-warning', 'badge-info', 'badge-success'];
                    for (let i = 0; i < questions.length; i++) {
                        questions[i].statusText = statusTexts[questions[i].status];
                        questions[i].statusClass = statusClasses[questions[i].status];
                        questions[i].tagImage = '/img/tags/' + questions[i].tags[0].id + '.jpg';

                        let now = new Date().getTime();
                        let pastTime = (now - new Date(questions[i].createdTime).getTime()) / 1000;
                        let createdTimeText = "未知时间";
                        if (pastTime < 60) { // 不足1分钟
                            createdTimeText = "刚刚";
                        } else if (pastTime < 60 * 60) { // 不足1小时
                            createdTimeText = parseInt(pastTime / 60) + "分钟前";
                        } else if (pastTime < 60 * 60 * 24) {
                            createdTimeText = parseInt(pastTime / 60 / 60) + "小时前";
                        } else {
                            createdTimeText = parseInt(pastTime / 60 / 60 / 24) + "天前";
                        }

                        questions[i].createdTimeText = createdTimeText;
                    }
                    questionsApp.questions = questions;
                    questionsApp.pageInfo = data;
                }
            });
        }
    },
    created: function () {
        this.loadMyQuestions();
    }
});

46. 关于Summernote的图片处理

使用Summernote富文本编辑器时,当需要处理图片时,会自动将图片转换为Base64编码,当提交问题时,图片的Base64编码会作为“问题正文”的一部分提交到服务器端,最终,会被存储到数据库中!使用这种做法,会急剧增加数据库所占用的存储空间,对数据库的检索性能也会产生影响,不利于数据库的管理和维护,同时,由于图片已经转换为Base64编码作为正文的一部分数据,也不利于管理图片!

Summernote允许在配置Summernote富文本编辑器时自定义回调函数,该函数会在用户填写正文时选择图片会自动调用,则开发人员可以配置这个回调函数,当用户选择图片后,将图片以文件的形式直接上传到服务器端,当上传成功后,再将图片的路径返回到客户端,插入到Summernote中即可!

最后,在Summernote组织的“问题正文”中,关于图片可能就只是一段例如<img src="http://localhost:8080/1.jpg />这样的代码,就能够减少数据库的存储数据量,同时,对于文件的管理也会非常直观。

47. 基于SpringMVC的文件上传

【本知识点的案例为:fileupload】

关于文件上传,在HTTP协议中规定:

  • 必须使用POST方式提交请求;
  • 在HTML表单中必须配置enctype="multipart/form-data"

另外,在HTML表单中必须使用<input type="file" />控件。

所以,可以将页面设计为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上传图片</title>
</head>
<body>
<h1>上传图片</h1>
<form action="/upload" method="post" enctype="multipart/form-data">
    <p>请选择您要上传的文件:<input type="file" name="image"></p>
    <p><input type="submit" value="上传"></p>
</form>
</body>
</html>

SpringMVC框架处理文件上传是基于commons-fileupload的,如果使用SpringMVC框架,需要自行添加这个依赖,如果使用SpringBoot框架则不需要,已经内置添加了。

在SpringMVC框架中,在控制器端会使用MultipartFile接口类型的参数来接收客户端提交的上传数据,在处理请求的方法中,直接声明这个接口类型的参数即可,参数名应该与客户端提交请求时的名称保持一致!在处理请求的过程中,调用MutlipartFile接口对象的void transferTo(File dest)方法就可以将图片保持到参数dest对应的文件位置。

可以在服务器端创建控制器处理上传请求:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@RestController
public class FileUploadController {

    @RequestMapping("/upload")
    public String upload(MultipartFile image) throws IOException {
        image.transferTo(new File("d:/1.jpg"));
        return "OK";
    }

}

注意:SpringBoot默认限制了上传文件的大小为1M / 10M(根据SpringBoot版本不同存在差异)。

关于文件名的处理:

  • 文件名必须保证唯一,不要出现“覆盖上传”的现象(即使你认为原有的文件没有用了,也不要覆盖);
  • 扩展名应该与原始扩展名(文件在客户端设备中的名称)保持一致,注意:如果某个文件全名中只有第1位是小数点,并没有更多的小数点,是表示该文件在Linux / MacOS中是隐藏文件,小数点右侧的并不是扩展名!

示例代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@RequestMapping("/upload")
public String upload(MultipartFile image) throws IOException {
    String parent = "d:/";

    // 处理文件名
    // 关于文件名的策略:时间 + 随机数
    // 无论当前上传功能是用于哪个用途,文件名必须唯一
    String filename = UUID.randomUUID().toString();

    // 处理扩展名
    // 获取原始文件名
    String originalFilename = image.getOriginalFilename();
    System.out.println("originalFilename=" + originalFilename);
    // 暂定扩展名空字符串
    String suffix = "";
    // 如果原始文件名中存在有效的扩展名,则截取
    int beginIndex = originalFilename.lastIndexOf(".");
    if (beginIndex > 0) {
        suffix = originalFilename.substring(beginIndex);
    }

    // 得到完整的文件名
    String child = filename + suffix;

    // 保存上传的文件
    image.transferTo(new File(parent, child));
    return "OK";
}

关于保存文件的路径,首先,所有的上传都是为了下载的,所以,必须保证上传的文件夹是可以被访问到的文件夹,例如将文件上传到Tomcat的部署文件夹中,对于使用SpringBoot开发项目来说,也可以理解为“需要将文件上传到static文件夹或webapp文件夹下”!

可选的解决方案有:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
String parent = request.getServletContext().getRealPath("20200725");
System.out.println("parent=" + parent);
File parentFile = new File(parent);
if (!parentFile.exists()) {
    parentFile.mkdirs();
}

以上做法是将文件直接上传到项目的webapp文件夹中,这样做不便于管理文件,因为项目文件和上传的文件都在同个文件夹之下!

SpringMVC / SpringBoot可以自定义“资源目录”,当某个文件夹被设置为“资源目录”时,该目录下的内容是可以直接通过HTTP协议进行访问的!相当于staticwebapp文件夹。

在SpringBoot项目的application.properties文件中进行配置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
spring.resources.static-locations=file:d:/upload

d:/upload就变成了“资源目录”,如果在这个文件夹中添加文件,是可以直接通过HTTP协议访问的!

然后,在application.properties中添加自定义配置,并将自定义配置值用于配置“资源目录”,并且,由于自定义了资源目录,原本static就不再是资源目录了,需要显式的指定:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
project.upload-location=d:/upload

spring.resources.static-locations[0]=classpath:/static
spring.resources.static-locations[1]=file:${project.upload-location}

在控制器中,可以直接读取到以上配置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Value("${project.upload-location}")
private String parent;

后续,使用以上parent作为上传的文件夹即可。

在处理上传时,关于MultipartFile的常用API有:

  • boolean isEmpty():判断上传的文件是否为空,如果在表单中没有选择文件,或选择的文件是0字节的,即为空;
  • long getSize():获取文件大小,以字节为单位;
  • String contentType:获取文档的MIME类型;
  • String getOriginalFilename():获取上传的文件的原始文件名;
  • void transferTo(File dest):保存上传的文件。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/08/01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
谷歌详解Android Wear:大大简化可穿戴产品交互
据国外媒体报道,在I/O开发者大会即将开幕之际,谷歌披露了更多有关Android Wear可穿戴产品界面如何运作的细节信息。 谷歌近日发布的一个YouTube视频对Android Wear软件开发工
大数据文摘
2018/05/21
8210
被媒体“冷落”的智能手表,还能火起来吗?
智能手表或者说Android Wear,在2013年、2014年被媒体捧得很高后,从去年开始便似乎热度骤降。近来,科技圈的目光似乎聚焦在听起来更加高大上的无人机、VR,那么智能手表就“钱”途暗淡了吗?
镁客网
2018/05/28
5520
腕上的未来——智能手表的演变与市场蓝图
智能手表,作为可穿戴设备中的明星产品,在过去十年间经历了从新奇概念到日常配件的惊人转变。它不仅代表着科技进步,更是个人健康管理、移动互联与时尚科技的交汇点。那么,智能手表如何发展至今?未来又将如何塑造我们的生活?本文将从技术演进与市场前景两方面进行探讨。
Echo_Wish
2025/03/20
990
腕上的未来——智能手表的演变与市场蓝图
BAT中为何只有百度做智能手表了?
2012年GoogleGlass推出让可穿戴设备进入大众视野,因为种种原因智能眼镜并未普及,智能手环和智能手表这两类可穿戴设备却后来居上。全球范围卖得最好的fitbit正在谋划IPO,国内小米手环卖到100万只,成为表现最好的可穿戴设备。2014年在业界翘首以盼之中,AppleWatch和Android Wear问世,这既是可穿戴业的利好,也为在移动互联领域打的水深火热的中国互联网公司开辟了一个新战场。就在业界普遍还在观望之时,百度几天前已经推出百度手表V1.0,率先破局。 面向智能手表平台的百度服务 百度
罗超频道
2018/04/28
7540
BAT中为何只有百度做智能手表了?
为什么大家都在卖智能手环?
点击标题下「大数据文摘」可快捷关注 当微软从斜刺里杀出来,直接跳进了智能移动硬件的行列,以Band手环切入的时候,又有很多人开始关注智能健康移动设备是不是也能撬动一个大市场?虽然手环是一个小玩意,在智能家电里也是微乎其微的存在,几乎可以忽略不计。尤其是小米、百度等切入到智能手环市场,打出的价格更是让人看不懂,几乎没有利润可言。但即使这样,也还是吸引到那么多的硬件、互联网企业趋之若鹜,那么这到底是为什么? 市场有多大? 据市场研究公司ABI Research的报告显示,在2014年第一季度,以智能手环为代表
大数据文摘
2018/05/22
9390
高通推出Snapdragon Wear 2100,新款智能可穿戴设备将可独立联网
近年来,智能穿戴设备的话题度已大不如从前。不过,有业内人士分析这不过是该市场迅速发展的“沉淀期”。最近,高通再次升级了其智能穿戴平台的解决方案,推出了Snapdragon Wear 2100。这个举动
镁客网
2018/05/28
7470
和苹果打对台,Meta也要做智能手表了!
2022年1月,荷兰科技博客LetsGoDigital在Facebook网站的官方账号上爆料:2021年的传闻是真的,Facebook真的要做智能手表了。
新智元
2022/02/24
5440
和苹果打对台,Meta也要做智能手表了!
智能手表续航太短?聊聊可穿戴设备的能耗管理黑科技
可穿戴设备的发展突飞猛进,但续航问题始终是个大麻烦。电池技术短期内没啥突破,那就只能在能耗管理上下功夫,让设备更省电、更智能。
Echo_Wish
2025/03/29
1190
智能手表续航太短?聊聊可穿戴设备的能耗管理黑科技
智能手表爆发在即,Apple Watch稳坐头把交椅
原创2015-03-05 罗超 2015年的智能手表热,难免让人想起2008年的智能手机。iPhone第一代07年发布,人们对其前景如何,莫衷一是,08年Android在G粉翘首以盼中面世,与Apple分庭抗礼;09年中国移动OPHONE算中国消费者市场关注Android的标志,最终完败成炮灰。直到2010年3G发牌前,智能手机对于不少人而言还是陌生名词,手机厂商、开发者、用户,都属于小众群体。今天,整个世界已经被智能手机重新塑造。我想,2015年就是智能手表的2008。 2015年智能手表进入爆发式增长
罗超频道
2018/04/28
9002
智能手表爆发在即,Apple Watch稳坐头把交椅
智能手表的下半场,机遇与挑战并存
随着移动通信、图像技术、人工智能等技术的不断发展及创新融合,在全球应用和体验式消费的驱动下,可穿戴设备迅速发展,已成为全球增长最快的高科技市场之一。
二山山记
2022/07/29
4620
抖音捧红的智能手表,现在更酷炫了
5 月 24 日,出门问问在北京召开发布会,一口气发布了五款产品,新一代智能手表 TicWatch Pro、小问音箱儿童版 TicKasa Fox、小问音箱时尚版 TicKasa Nano、智能单耳耳机 TicPod Solo、以及 AI 芯片模组“问芯”。
AI科技大本营
2018/07/23
5180
抖音捧红的智能手表,现在更酷炫了
APP与智能手表的通讯
APP与智能手表的通讯方式多样,选择取决于具体需求,如功耗、传输速度和距离等。蓝牙(尤其是BLE)是最常用的方式,Wi-Fi和蜂窝网络则适合需要高速或远程通讯的场景。
数字孪生开发者
2025/03/06
1160
APP与智能手表的通讯
400+团队乱战儿童智能手表市场:互联网玩家将占上风
智能硬件浪潮袭来,最受关注的莫过于可穿戴设备和智能家居,而最大的黑马便诞生于可穿戴设备领域:儿童智能手表。现在这一市场玩家越来越多,大体可分为三大阵营:传统硬件大厂、山寨白牌公司和互联网巨头。整个行业大概有400个玩家,争夺着儿童的手腕,不过,最终将会赢得这一“黑马市场”的,很可能会是互联网玩家。 儿童智能手表为什么会成为黑马? 2015年Q3,苹果智能手表450万部,全球智能手表卖出600万部,当季,儿童智能手表在中国市场卖出大约900万部,就是说,儿童智能手表仅仅是在中国市场,都比面向成人的智能手表在
罗超频道
2018/04/27
8510
400+团队乱战儿童智能手表市场:互联网玩家将占上风
儿童智能手表如何做?糖猫知道
谁是2015年智能硬件领域的明星产品?Apple Watch。就像iPhone之于智能手机一样,Apple Watch掀起了一股智能手表大众化的潮流。不过,智能手表领域有一个隐形冠军被大家忽略了——儿童智能手表。一位硬件行业内人士不久之前向笔者透露,儿童智能手表有望成为2015年的爆款产品,很多深圳厂商都瞄准了这个品类准备大干一场,大量玩家纷纷涌入,而搜狗等互联网公司布局更早,一场厮杀在所难免。 儿童智能硬件成万物互联重头戏 儿童智能手表拥有庞大的潜在消费群。多数儿童智能手表均是面向6-12岁的孩子,即小学
罗超频道
2018/04/28
8640
儿童智能手表如何做?糖猫知道
谷歌或将推出一款基于AI的健身教练Google Coach
此前有报道称,苹果可能正在开发一款用于健康数据分析的协处理器,而目前有消息指出,谷歌的一款名为“Google Coach”的产品由AI提供动力。
AiTechYun
2018/09/26
7510
谷歌或将推出一款基于AI的健身教练Google Coach
Goolge 回归中国尘埃落定,携手出门问问发布应用商店
1月25 日,国内语音搜索和可穿戴厂商出门问问联合 Google 在北京召开发布会,推出了出门问问应用商店。据悉,问问应用商店是一款专门针对智能手表的应用,它成为谷歌在中国应用市场上支持的首个官方合作
新智元
2018/03/14
1.1K0
Goolge 回归中国尘埃落定,携手出门问问发布应用商店
智能手表:华米稳、华为猛
随着科技的进步与发展,消费电子产品的功能和品类越来越丰富,人们也愈发习惯使用消费电子产品了。消费电子产品一词听起来陌生,实际上却离人们很近,比如智能手机、笔记本电脑等等,都属于消费电子产品。现如今,以智能手机为代表的消费电子产品已经逐渐深入到了人们的日常生活中,并且扮演着愈发重要的角色。
刘旷
2023/08/23
2120
智能手表,不再只是手机品牌的“附属品”
当下或许正是一个不错的时机,随着通信、移动技术的及产品本身功能的不断完善,智能手表开始被越来越多消费者认可。数据显示,2021年中国智能手表市场规模达到295亿元,2022年一季度全球智能手表相比2021年第一季度同比增加了13%。
用户2908108
2022/12/17
5330
智能手表,不再只是手机品牌的“附属品”
视觉回顾智能手表的前世今生
摘自:网易科技 2014年:LG G Watch R LG G Watch R于2014年上市,搭载Android Wear系统,属于G Watch的后续版本。它配备1.2 GHz处理器、4GB的内部存储空间以及320x320像素显示屏。LG G Watch R不仅仅能够跟相配的Android智能手机通讯,还能够通过Google Now处理语音指令。它还拥有现代智能手表的一些常见功能,如心率监测、脉搏监测和运动追踪。 该设备的零售价为295美元。其最令人称道的地方是时尚大气的设计,不过它面临的市场竞争非常激
大数据文摘
2018/05/21
8730
闻泰科技智能手表业务发展迅猛,与荣耀联合打造的荣耀手表4
7月12日晚,荣耀举办全场景新品发布会,正式发布全新一代智能手表——荣耀手表4。据了解,这款支持eSIM独立通话、实现10天超长续航、并行业首发一表双待的重磅新品,由闻泰科技和荣耀联合打造,闻泰科技为荣耀手表4提供从研发设计到生产制造以及供应链管理的一站式ODM解决方案。
芯智讯
2023/08/09
2640
闻泰科技智能手表业务发展迅猛,与荣耀联合打造的荣耀手表4
推荐阅读
相关推荐
谷歌详解Android Wear:大大简化可穿戴产品交互
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验