StarBlog Development Notes (20): Image Display Optimization
Preface My server bandwidth is relatively high, so when visiting the blog deployed on it I barely notice any loading delay, and I didn't optimize images. But recently some friends said the blog's images load rather slowly, so let's improve the image optimization~ Currently two things need to be improved Image waterfall layout Image thumbnails Image waterfall layout I introduced the waterfall layout in a previous article: Developing a Blog Project with .NetCore - StarBlog - (10) Image Waterfall Layout But at the time I directly used the Bootstrap5 sample code, which had occasional display misalignment
目录7 个章节
Preface
My server bandwidth is relatively high, so when visiting the blog deployed on it I barely notice any loading delay, and I didn't optimize images. But recently some friends said the blog's images load rather slowly, so let's improve the image optimization~
Currently two things need to be improved
- Image waterfall layout
- Image thumbnails
Image Waterfall Layout
I introduced the waterfall layout in a previous article: Developing a Blog Project with .NetCore - StarBlog - (10) Image Waterfall Layout
But at the time I directly used the Bootstrap5 sample code, which had a bug where items would occasionally display misaligned.
I checked the masonry-layout official site again and found the cause of this bug: the waterfall layout was rendered before the images had finished loading, and since the images vary in size, this caused the final display to be misaligned.
The fix is also simple: use the imagesLoaded library, which has an event that fires after all images have loaded; re-render the waterfall layout in the event handler, and you're done~
The masonry-layout library doesn't bundle imagesLoaded; you have to install it yourself to use it.
The version I used is 5.0.0
yarn add imagesloaded
Configure the path in gulpfile.js of StarBlog.Web
// 使用 npm 下载的前端组件,自定义存放位置
const customLibs = [
// ...
{name: 'imagesloaded', dist: './node_modules/imagesloaded/*.js'},
]
Then run gulp move
Next, modify the photo page StarBlog.Web/Views/Photography/Index.cshtml
Import the JS dependencies. I also used the environment tag helper here, which can include different files based on the environment. In the development environment include the full JS files; in the production environment include the *.min files.
@section bottom {
<environment include="Development">
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/masonry-layout/dist/masonry.pkgd.js"></script>
<script src="~/lib/imagesloaded/imagesloaded.pkgd.js"></script>
</environment>
<environment exclude="Development">
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/masonry-layout/dist/masonry.pkgd.min.js"></script>
<script src="~/lib/imagesloaded/imagesloaded.pkgd.min.js"></script>
</environment>
<script src="~/js/photo.js"></script>
}
Change the image list part of the code
<div id="photo-grid" class="row">
@foreach (var photo in Model.Photos) {
<div class="col-sm-6 col-lg-4 mb-4 grid-item">
<partial name="Widgets/PhotoCard" model="photo"/>
</div>
}
</div>
Then the JS code in js/photo.js
// init Masonry
let $grid = $('#photo-grid').masonry({
itemSelector: '.grid-item',
percentPosition: true,
})
// layout Masonry after each image loads
$grid.imagesLoaded().progress( function() {
$grid.masonry('layout');
});
Done~
Image Thumbnails
At first I used the SixLabors.ImageSharp.Web library
Pretty convenient, used in the form of middleware
After installing via Nuget, configure it in program.cs
// 注册服务
builder.Services.AddImageSharp();
// 添加中间件
app.UseImageSharp();
After that, you can append parameters like width=xxx&format=xxx to any image file URL to resize and convert the format~
I made each image in the photo list into a partial view component, in the file StarBlog.Web/Views/Shared/Widgets/PhotoCard.cshtml
To turn this into a thumbnail, you just need to modify the img element
Now each image thumbnail has a width of 300 (adaptive width), converted to webp format
<img class="bd-placeholder-img card-img-top" src="/media/@Model.FilePath?width=300&format=webp" alt="">
This middleware has many other parameters you can set; I only used two. See the docs for details: https://docs.sixlabors.com/articles/imagesharp.web/processingcommands.html
Progressive JPEG
Using the ImageSharp.Web middleware was already good enough
But there was one shortcoming: it doesn't support generating progressive JPEG images, so you can't show a blurry outline first and then gradually sharpen when loading an image; instead it loads line by line from top to bottom, which doesn't look great~
So I used the Magick.NET library to generate progressive JPEG images.
This library has three versions: Q8, Q16, Q16-HDRI
According to the official docs, I chose the recommended Q8 version, which has the best performance (image quality doesn't matter anymore)
Install the Magick.NET-Q8-AnyCPU library via nuget
Modify StarBlog.Web/Services/PhotoService.cs
Now I'm going to write a method to generate thumbnails
/// <summary>
/// 生成Progressive JPEG缩略图 (使用 MagickImage)
/// </summary>
public async Task<byte[]> GetThumb(string id, int width=300) {
var photo = await _photoRepo.Where(a => a.Id == id).FirstAsync();
using (var image = new MagickImage(GetPhotoFilePath(photo))) {
image.Format = MagickFormat.Pjpeg;
image.Resize(width,0);
return image.ToByteArray();
}
}
Then write an API
Edit StarBlog.Web/Apis/PhotoController.cs
[HttpGet("{id}/Thumb")]
public async Task<IActionResult> GetThumb(string id, [FromQuery] int width = 300) {
var data = await _photoService.GetThumb(id, width);
return new FileContentResult(data, "image/jpeg");
}
Done.
Now you just need to visit /Api/Photo/{图片ID}/Thumb?width=300 to generate a progressive JPEG thumbnail~
Finally, update the StarBlog.Web/Views/Shared/Widgets/PhotoCard.cshtml component
<img class="bd-placeholder-img card-img-top" alt=""
src="@Url.Action("GetThumb", "Photo", new {id = Model.Id, width = 300})">
okk~
Summary
Compared to the ImageSharp.Web middleware's built-in caching, this thumbnail method I wrote myself is rather crude, and it's generated dynamically on every request, which puts some pressure on the server.
But it doesn't have Progressive JPEG, and this missing feature is really frustrating; I can only hope they implement it soon~
Otherwise I'll have to implement caching myself to improve performance~
Series Articles
- Developing a Blog Project with .NetCore - StarBlog - (1) Why Do You Need to Write Your Own Blog?
- Developing a Blog Project with .NetCore - StarBlog - (2) Environment Setup and Creating the Project
- Developing a Blog Project with .NetCore - StarBlog - (3) Model Design
- Developing a Blog Project with .NetCore - StarBlog - (4) Batch Import of Markdown Blog Posts
- Developing a Blog Project with .NetCore - StarBlog - (5) Starting to Build the Web Project
- Developing a Blog Project with .NetCore - StarBlog - (6) Page Development: Blog Post List
- Developing a Blog Project with .NetCore - StarBlog - (7) Page Development: Post Detail Page
- Developing a Blog Project with .NetCore - StarBlog - (8) Displaying Category Hierarchy
- Developing a Blog Project with .NetCore - StarBlog - (9) Batch Import of Images
- Developing a Blog Project with .NetCore - StarBlog - (10) Image Waterfall Layout
- Developing a Blog Project with .NetCore - StarBlog - (11) Implementing Visit Statistics
- Developing a Blog Project with .NetCore - StarBlog - (12) Dynamic Compilation of Razor Pages
- Developing a Blog Project with .NetCore - StarBlog - (13) Adding a Friend Links Feature
- Developing a Blog Project with .NetCore - StarBlog - (14) Implementing Theme Switching
- Developing a Blog Project with .NetCore - StarBlog - (15) Generating Random-Size Images
- Developing a Blog Project with .NetCore - StarBlog - (16) Some New Features (Monitoring/Statistics/Configuration/Initialization)
- Developing a Blog Project with .NetCore - StarBlog - (17) Automatically Downloading External Images in Posts
- Developing a Blog Project with .NetCore - StarBlog - (18) Packaging and Uploading Local Typora Posts
- Developing a Blog Project with .NetCore - StarBlog - (19) Exploring Markdown Rendering Solutions
- Developing a Blog Project with .NetCore - StarBlog - (20) Image Display Optimization
References
_EOF_
StarBlog Development Notes (20): Image Display Optimization
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
StarBlog博客开发笔记(21):开始开发RESTFul接口
前言 最近电脑坏了,开源项目的进度也受到一些影响 这篇酝酿很久了,作为本系列第二部分(API接口开发)的第一篇,得想一个好的开头,想着想着就鸽了好久,索性不扯那么多了,直接开写吧~ 关于RESTFul 网上很多相关的文章都要把RESTFul历史来龙去脉给复制一遍,所以我这就不重复了,现在主要的HTTP接口风格就俩:RPC和RESTFul。 举个例子就可以看出这俩的区别 RPC风格 分别是增删改查的
2023-01-02 · StarBlog博客开发笔记 · 约 1 分钟
StarBlog博客开发笔记(18):实现本地Typora文章打包上传
前言 九月太忙,只更新了三篇文章,本来这个功能是从九月初就开始做的,结果一直拖到现在国庆假期才有时间完善并且写文章~ 之前我更新了几篇关于 Python 的文章,有朋友留言问是不是不更新 .Net 了,那肯定不能啊,我只能说**「我 全 都 要」**,所以我反手就更新了一篇Asp-Net-Core开发笔记。 然后顺便立个Flag:今年底前完成StarBlog系列文章的主体部分(即API开发+后台前
2022-10-06 · StarBlog博客开发笔记 · 约 1 分钟
StarBlog博客开发笔记(2):环境准备和创建项目
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 ... 基于.NetCore开发博客项目 StarBlog - 接口返回值包装 基于.NetCore开发博客项目 StarBlog - 上传图片功能 基于.NetCore开发博客项目 StarBlog - 身份认证
2022-04-22 · StarBlog博客开发笔记 · 约 1 分钟
StarBlog博客开发笔记(24):统一接口数据返回格式
前言 开发接口,是给客户端(Web前端、App)用的,前面说的RESTFul,是接口的规范,有了统一的接口风格,客户端开发人员在访问后端功能的时候能更快找到需要的接口,能写出可维护性更高的代码。 而接口的数据返回格式也是接口规范的重要一环,不然一个接口返回JSON,一个返回纯字符串,客户端对接到数据时一脸懵逼,没法处理啊。 合格的接口返回值应该包括状态码、提示信息和数据。 就像这样: { st
2023-01-02 · StarBlog博客开发笔记 · 约 1 分钟
讨论
共 0 条加载评论中…