/羊城派記者 陳亮
圖/陳亮 趙思穎
6月28日傍晚,廣州多地均可見一片七彩的云朵,猶如肥皂泡漂浮于白色的云層之間,在夕陽余暉的映照下發出絢麗多姿的光芒,持續了大約20分鐘,非常罕見且極具觀賞性。記者日前采訪了中山大學大氣科學學院副教授伊炳祺,原來,這種像彩虹一樣的美麗云霞學名叫“虹彩云”(iridescence, irisation, rainbow clouds)。
據伊炳祺老師觀察,云中出現五顏六色的色彩,有時色彩是混合在一起的,有時則是平行帶狀出現在云的邊緣(例如云頂部)。其中,綠色和粉色較為常見。云中的虹彩最常出現在靠近太陽附近的地方,而且在太陽被遮擋住的情況下最為清晰可見,但在距離太陽較遠的地方也是可以出現的。
圖/陳亮 趙思穎
這種“天降祥云”的大氣現象是怎么產生的呢?伊炳祺老師指出,太陽光的衍射和干涉現象是虹彩出現的主要原因,其中在相對太陽10度角以內,衍射起主要作用;大于10度,干涉往往是主要影響因素。
圖/陳亮 趙思穎
伊炳祺老師進一步分析稱,這些虹彩色是扭曲了的、或破碎的華現象(同樣由于太陽光的衍射現象所引起)。在大氣中的云滴或冰晶粒子尺寸比較一致的時候,規則的華現象才會出現。
當云發生生消變化時,這些色彩也會隨之扭曲變形或消失。高積云、卷積云,尤其是莢狀云最容易出現虹彩現象。虹彩現象也經常出現在云正在生成的時候,因為此時云滴粒子常常是具有相近的尺寸,而且此時云并不太厚。
圖/陳亮 趙思穎
在大家的印象中,這樣的“七彩祥云”似乎并不多見。但伊炳祺老師卻告訴記者,相比于出現在接近極地地區的貝母云(nacreous, mother-of-pearl clouds)(特殊的虹彩云),虹彩云并不算非常罕見。實際上,虹彩云在全年、各個緯度、各種天氣條件下都可以出現。尤其在炎熱、濕潤的下午天更容易出現,甚至在火箭發射飛行的尾跡云中也曾觀察到有虹彩出現。
參考資料:
(更多新聞資訊,請關注羊城派 pai.ycwb.com)
來源 | 羊城派
責編 | 江文華 實習生 | 周子厚
| 王成
本文轉載自SegmentFault
WebP格式介紹
WebP是Google開發的一種新的圖片格式,它支持有損壓縮、無損壓縮和透明度,壓縮后的文件大小比JPEG、PNG等都要小。所以可以節省帶寬,減少頁面載入時間,節省用戶的流量。
Android和iOS的App只要引入Google提供的解碼庫,都可以很輕松的支持WebP格式。不過在Web上,WebP的支持還不是很廣泛。根據Can I Use的數據,目前只有Chrome、Opera瀏覽器,以及Android的WebView是支持WebP的。但是WebP圖片有這么多優點,我們能不能在Web頁面中使用呢?可以。這篇文章就來討論一下這個問題。
把已有的圖片轉換為WebP格式
要使用WebP格式,需要將你網站用到的圖片都制作一份WebP格式的版本,如果你使用CDN服務商,它們一般都會提供轉碼到WebP格式的選項。如又拍云:
增加這樣的配置后,我們可以通過給圖片URL加上相應的后綴,來使用WebP格式的版本資源。
你也可以使用Webpack、Gulp的插件來批量轉換圖片格式。這里不贅述。
在瀏覽器中使用WebP格式
因為不是所有瀏覽器都支持WebP格式,我們就有兩種思路:一個是只在支持WebP格式的瀏覽器中使用WebP格式;一個是讓不支持WebP格式的瀏覽器可以支持WebP。
姿勢一: <Picture>標簽
<Picture>是HTML5中的一個新標簽,類似<Video>它也可以指定多個格式的資源,由瀏覽器選擇自己支持的格式進行加載。
<picture class="picture">
<source type="image/webp" srcset="image.webp">
<img class="image" src="image.jpg">
</picture>
如果瀏覽器支持WebP格式,就會加載Image.webp,否則會加載Image.jpg。
即使瀏覽器不支持<Picture>標簽,圖片仍然會正常顯示,只是CSS可能無法正確選取到Picture元素。比如在IE8中,下面的CSS就不會起作用:
.picture img { width: 100px; height: 100px;}
但是可以這樣來給圖片寫樣式:
.image { width: 100px; height: 100px;}
即使瀏覽器使用的是WebP格式的圖片,最終還是會應用img元素的樣式。
不過只要使用了HTML5Shiv,使舊的瀏覽器支持這個標簽,CSS選擇器就可以正常使用了。這種方法是最簡單的,但是不能作用于CSS中的圖片(如背景)。
姿勢二:使用JS替換圖片的URL。
我們有很多的頁面往往會用到圖片的“懶加載”——通常是把圖片的URL放在Img元素的一個自定義屬性中,然后用JS在適當的時機將URL賦值給SRC屬性。用類似的原理,我們可以根據瀏覽器是否支持WebP格式,給Img元素賦予不同的SRC值。
首先我們需要用JS來判斷瀏覽器是否支持WebP格式,方法是給瀏覽器一個WebP格式的圖片,看瀏覽器能否正確渲染。這種方法是異步的,所以需要把后續的操作寫在回調函數中。我們可以將結果存儲在LocalStorage中,這樣之后就不用再次檢查了。
function checkWebp(callback) { var img=new Image();
img.onload=function () { var result=(img.width > 0) && (img.height > 0);
callback(result);
};
img.onerror=function () {
callback(false);
};
img.src='data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA';
}
然后用下面的代碼來根據是否支持WebP替換相應的SRC。
function showImage(useWebp){ var imgs=Array.from(document.querySelectorAll('img'));
imgs.forEach(function(i){ var src=i.attributes['data-src'].value; if (useWebp){
src=src.replace(/\.jpg$/, '.webp');
}
i.src=src;
});
}
checkWebp(showImage);
這種方式的優點是可以與已有的懶加載函數相結合。而且使用JS,我們還可以處理CSS中的圖片(如背景圖等)。
姿勢三:使用JS解碼WebP圖片
既然WebP的解碼器是開源的,那么能否用JS來實現呢?當然可以,有人就用JS寫出了WebP的解碼器。引入這個JS庫,就是將所有的WebP圖片用JS解碼后轉換為Base64,然后替換掉原來的URL,這樣就可以讓原本不支持WebP的瀏覽器正常顯示WebP了。這個庫的使用方法非常簡單,看網頁的說明即可。
這種方法的缺點是,因為JS要解碼WebP圖片,需要在此異步請求SRC中的URL(不過因為圖片本身之前被下載了一次,直接使用了緩存);而且JS解碼比較慢,對性能有影響,可能需要一段時間才能顯示出圖片來。
以上就是在瀏覽器中使用WebP圖片的幾種方法,可以根據自己的實際情況選用。在我們的實踐中,使用了WebP格式后,圖片的體積普遍縮小了1/3以上,既加快了加載的速度,還節省了用戶的流量,我們十分推薦從現在就開始使用這種格式。
又小拍也關注WebP一段時間呢,不但關注了WebP,還關注了動態WebP,不久之后會有驚喜帶給大家哦。
又拍云的處理功能實在太豐富,在圖片處理方面,略縮圖任意尺寸更改,全網一鍵更新所有圖片,打水印,URL防盜鏈等。最近上線的又拍直播云,除了直播加速、推拉流外,更具有豐富的美顏、濾鏡、水印、防盜鏈、鑒黃、禁播等功能,幫助直播平臺快速上線直播業務,快來試試吧~
MinIO 是一款高性能、分布式的對象存儲系統。它是一款軟件產品, 可以100%的運行在標準硬件上。即X86等低成本機器也能夠很好的運行MinIO。
MinIO與傳統的存儲和其他的對象存儲不同的是:它一開始就針對性能要求更高的私有云標準進行軟件架構設計。因為MinIO一開始就只為對象存儲而設計。所以他采用了更易用的方式進行設計,它能實現對象存儲所需要的全部功能,在性能上也更加強勁,它不會為了更多的業務功能而妥協,失去MinIO的易用性、高效性。 這樣的結果所帶來的好處是:它能夠更簡單的實現具有彈性伸縮能力的原生對象存儲服務。
MinIO在傳統對象存儲用例(例如輔助存儲,災難恢復和歸檔)方面表現出色。同時,它在機器學習、大數據、私有云、混合云等方面的存儲技術上也獨樹一幟。當然,也不排除數據分析、高性能應用負載、原生云的支持。
在中國:阿里巴巴、騰訊、百度、中國聯通、華為、中國移動等等9000多家企業也都在使用MinIO產品。
使用如下命令快速安裝一個單機minio
# 下載 minio
wget https://dl.min.io/server/minio/release/linux-amd64/minio
# 添加可執行權限
chmod +x minio
# 設置登錄minio的 access key
export MINIO_ACCESS_KEY=minioadmin
# 設置登錄minio的 secret key
export MINIO_SECRET_KEY=minioadmin
# 啟動 minio
./minio server /data
安裝后使用瀏覽器訪問http://IP地址:9000,如果可以訪問,則表示minio已經安裝成功。輸入上面自定義的access key 和 secret key就可以登錄了。
登錄右下角加號創建mybucket桶
開放 mybucket 讀寫權限
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>7.0.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
編輯配置文件application.yml修改MinIO相關配置
server:
port: 8080
spring:
application:
name: minio-test
thymeleaf:
cache: false
servlet:
multipart:
max-file-size: 10MB
max-request-size: 100MB
# minio 連接參數
minio:
endpoint: 192.168.20.111
port: 9000
accessKey: minioadmin
secretKey: minioadmin
bucketName: mybucket
連接 MinIO 配置
@Data
@ConfigurationProperties(prefix="minio")
@Component
public class MinioProp {
private String endpoint;
private String accesskey;
private String secretKey;
private int port;
}
創建 MinioClient
@Component
public class MinioConfiguration {
@Autowired
private MinioProp minioProp;
@Bean
public MinioClient minioClient() throws InvalidPortException, InvalidEndpointException {
//MinioClient client=new MinioClient(minioProp.getEndpoint(), minioProp.getAccesskey(), minioProp.getSecretKey());
MinioClient client=MinioClient.builder().endpoint(minioProp.getEndpoint(), minioProp.getPort(), false).credentials(minioProp.getAccesskey(), minioProp.getSecretKey()).build();
return client;
}
}
MinIO 查看桶列表,存入,刪除 操作 MinioController
@Slf4j
@RestController
public class MinioController {
@Autowired
private MinioClient minioClient;
private static final String MINIO_BUCKET="mybucket";
@GetMapping("/list")
public List<Object> list(ModelMap map) throws Exception {
Iterable<Result<Item>> myObjects=minioClient.listObjects(MINIO_BUCKET);
Iterator<Result<Item>> iterator=myObjects.iterator();
List<Object> items=new ArrayList<>();
String format="{'fileName':'%s','fileSize':'%s'}";
while (iterator.hasNext()) {
Item item=iterator.next().get();
items.add(JSON.parse(String.format(format, item.objectName(), formatFileSize(item.size()))));
}
return items;
}
@PostMapping("/upload")
public Res upload(@RequestParam(name="file", required=false) MultipartFile[] file) {
Res res=new Res();
res.setCode(500);
if (file==null || file.length==0) {
res.setMessage("上傳文件不能為空");
return res;
}
List<String> orgfileNameList=new ArrayList<>(file.length);
for (MultipartFile multipartFile : file) {
String orgfileName=multipartFile.getOriginalFilename();
orgfileNameList.add(orgfileName);
try {
InputStream in=multipartFile.getInputStream();
minioClient.putObject(MINIO_BUCKET, orgfileName, in, new PutObjectOptions(in.available(), -1));
in.close();
} catch (Exception e) {
log.error(e.getMessage());
res.setMessage("上傳失敗");
return res;
}
}
Map<String, Object> data=new HashMap<String, Object>();
data.put("bucketName", MINIO_BUCKET);
data.put("fileName", orgfileNameList);
res.setCode(200);
res.setMessage("上傳成功");
res.setData(data);
return res;
}
@RequestMapping("/download/{fileName}")
public void download(HttpServletResponse response, @PathVariable("fileName") String fileName) {
InputStream in=null;
try {
ObjectStat stat=minioClient.statObject(MINIO_BUCKET, fileName);
response.setContentType(stat.contentType());
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
in=minioClient.getObject(MINIO_BUCKET, fileName);
IOUtils.copy(in, response.getOutputStream());
} catch (Exception e) {
log.error(e.getMessage());
} finally {
if (in !=null) {
try {
in.close();
} catch (IOException e) {
log.error(e.getMessage());
}
}
}
}
@DeleteMapping("/delete/{fileName}")
public Res delete(@PathVariable("fileName") String fileName) {
Res res=new Res();
res.setCode(200);
try {
minioClient.removeObject(MINIO_BUCKET, fileName);
} catch (Exception e) {
res.setCode(500);
log.error(e.getMessage());
}
return res;
}
private static String formatFileSize(long fileS) {
DecimalFormat df=new DecimalFormat("#.00");
String fileSizeString="";
String wrongSize="0B";
if (fileS==0) {
return wrongSize;
}
if (fileS < 1024) {
fileSizeString=df.format((double) fileS) + " B";
} else if (fileS < 1048576) {
fileSizeString=df.format((double) fileS / 1024) + " KB";
} else if (fileS < 1073741824) {
fileSizeString=df.format((double) fileS / 1048576) + " MB";
} else {
fileSizeString=df.format((double) fileS / 1073741824) + " GB";
}
return fileSizeString;
}
}
Res返回數據封裝
@lombok.Data
@AllArgsConstructor
@NoArgsConstructor
public class Res implements Serializable {
private static final long serialVersionUID=1L;
private Integer code;
private Object data="";
private String message="";
}
路由文件 RouterController
@Controller
public class RouterController {
@GetMapping({"/", "/index.html"})
public String index() {
return "index";
}
@GetMapping({"/upload.html"})
public String upload() {
return "upload";
}
}
啟動類
@SpringBootApplication
@EnableAutoConfiguration
@ComponentScan(basePackages="com.minio")
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
前端 列表頁面 src\main\resources\templates\index.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8"/>
<title>圖片列表</title>
<link rel="stylesheet" href="http://cdn.staticfile.org/element-ui/2.13.1/theme-chalk/index.css">
</head>
<body>
<div id="app">
<el-link icon="el-icon-upload" href="/upload.html">上傳圖片</el-link>
<br/>
<el-table :data="results" stripe style="width: 60%" @row-click="preview">
<el-table-column type="index" width="50"></el-table-column>
<el-table-column prop="fileName" label="文件名" width="180"></el-table-column>
<el-table-column prop="fileSize" label="文件大小"></el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<a :href="'/download/' + scope.row.fileName + ''" class="el-icon-download">下載</a>
<a :href="'/delete/' + scope.row.fileName + ''" @click.prevent="deleteFile($event,scope.$index,results)"
class="el-icon-delete">刪除</a>
</template>
</el-table-column>
</el-table>
<br/>
<el-link icon="el-icon-picture">預覽圖片</el-link>
<br/>
<div class="demo-image__preview" v-if="previewImg">
<el-image style="width: 100px; height: 100px" :src="imgSrc" :preview-src-list="imgList"></el-image>
</div>
</div>
<script src="http://cdn.staticfile.org/vue/2.6.11/vue.min.js"></script>
<script src="http://cdn.staticfile.org/axios/0.19.2/axios.min.js"></script>
<script src="http://cdn.staticfile.org/element-ui/2.13.1/index.js"></script>
<script>
new Vue({
el: '#app',
data: {
bucketURL: 'http://192.168.20.111:9000/mybucket/',
previewImg: true,
results: [],
imgSrc: '',
imgList: []
},
methods: {
init() {
axios.get('/list').then(response=> {
this.results=response.data;
if (this.results.length==0) {
this.imgSrc='';
this.previewImg=false;
} else {
for (var i=0; i < this.results.length; i++) {
this.imgList.push(this.bucketURL + this.results[i].fileName);
if (i==0) {
this.imgSrc=this.bucketURL + this.results[0].fileName;
}
}
}
});
},
preview(row, event, column) {
this.imgSrc=this.bucketURL + row.fileName;
this.previewImg=true;
},
deleteFile(e,index,list) {
axios.delete(e.target.href, {}).then(res=> {
if (res.data.code==200) {
this.$message('刪除成功!');
list.splice(index, 1);
this.previewImg=false;
} else {
this.$message('刪除失敗!');
}
});
}
},
mounted() {
this.init();
}
});
</script>
</body>
</html>
前端上傳頁面 src\main\resources\templates\upload.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>圖片上傳</title>
<link rel="stylesheet" type="text/css" href="http://cdn.staticfile.org/webuploader/0.1.5/webuploader.css">
<script type="text/javascript" src="https://cdn.staticfile.org/jquery/3.5.0/jquery.min.js"></script>
<script type="text/javascript" src="http://cdn.staticfile.org/webuploader/0.1.5/webuploader.min.js"></script>
</head>
<body>
<div id="uploader-demo">
<div id="fileList" class="uploader-list"></div>
<div id="filePicker">選擇圖片</div>
</div>
<br/>
<a href="/index.html">返回圖片列表頁面</a>
<script type="text/javascript">
var uploader=WebUploader.create({
auto: true,
swf: 'http://cdn.staticfile.org/webuploader/0.1.5/Uploader.swf',
server: '/upload',
pick: '#filePicker',
accept: {
title: 'Images',
extensions: 'gif,jpg,jpeg,bmp,png',
mimeTypes: 'image/*'
}
});
uploader.on('fileQueued', function (file) {
var $li=$(
'<div id="' + file.id + '" class="file-item thumbnail">' +
'<img>' +
'<div class="info">' + file.name + '</div>' +
'</div>'
),
$img=$li.find('img');
var $list=$("#fileList");
$list.append($li);
uploader.makeThumb(file, function (error, src) {
if (error) {
$img.replaceWith('<span>不能預覽</span>');
return;
}
$img.attr('src', src);
}, 100, 100);
});
uploader.on('uploadProgress', function (file, percentage) {
var $li=$('#' + file.id),
$percent=$li.find('.progress span');
if (!$percent.length) {
$percent=$('<p class="progress"><span></span></p>')
.appendTo($li)
.find('span');
}
$percent.css('width', percentage * 100 + '%');
});
uploader.on('uploadSuccess', function (file) {
$('#' + file.id).addClass('upload-state-done');
});
uploader.on('uploadError', function (file) {
var $li=$('#' + file.id),
$error=$li.find('div.error');
if (!$error.length) {
$error=$('<div class="error"></div>').appendTo($li);
}
$error.text('上傳失敗');
});
uploader.on('uploadComplete', function (file) {
$('#' + file.id).find('.progress').remove();
});
</script>
</body>
</html>
運行項目
列表頁面
參考博客:
https://blog.csdn.net/jianzhang11/article/details/105672261
*請認真填寫需求信息,我們會在24小時內與您取得聯系。