首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

获取视频跟图片的缩略图

2013-06-26 
获取视频和图片的缩略图??????????????????????? + image_path + ---)??????????? HashMapString, Str

获取视频和图片的缩略图
??????????????????????? + image_path + "---");
??????????? HashMap<String, String> hash = new HashMap<String, String>();
??????????? hash.put("image_id", image_id + "");
??????????? hash.put("path", image_path);
??????????? list.add(hash);

??????? } while (cur.moveToNext());

???? }
}
?????? String[] projection = { Media._ID, Media.DATA }; ?
?????? Cursor cursor = cr.query(Media.EXTERNAL_CONTENT_URI, projection, ?
???????????????????????????? Media._ID + "=" + image_id, null, null); ?
?????? if (cursor != null) { ?
?????????? cursor.moveToFirst(); ?
?????????? String path = cursor.getString(cursor.getColumnIndex(Media.DATA)); ?
?????????? Intent intent = new Intent(ThumbnailActivity.this, ImageViewer.class); ?
?????????? intent.putExtra("path", path); ?
?????????? startActivity(intent); ?
?????? } else { ?
?????????? Toast.makeText(ThumbnailActivity.this, "Image doesn't exist!", ?
???????????????????????????????? Toast.LENGTH_SHORT).show(); ?
????????? }
????? } ?
};
有关具体的缩略图可以通过getThumbnail(ContentResolver cr, long origId, int kind, BitmapFactory.Options options) 或getThumbnail(ContentResolver cr, long origId, long groupId, int kind, BitmapFactory.Options options) 方法获取,这两种方法返回Bitmap类型,而缩略图的分辨率可以从HEIGHT和WIDTH两个字段提取,在Android上缩略图分为两种,通过读取 KIND字段来获得,分别为MICRO_KIND和MINI_KIND 分别为微型和迷你两种缩略模式,前者的分辨率更低。这样我们平时获取文件系统的某个图片预览时,可以直接调用系统缩略图,而不用自己重新计算。

缩略图保存在SD卡的DCIM目录,里面的.thumbnails是图片的,而.video_thumbnails是视频的,这两个文件夹为隐藏属性
--------------------------------------------------
从Android2.2开始系统新增了一个缩略图ThumbnailUtils类,位于framework的 android.media.ThumbnailUtils位置,可以帮助我们从mediaprovider中获取系统中的视频或图片文件的缩略图,该类提供了三种静态方法可以直接调用获取。

1. static Bitmap createVideoThumbnail(String filePath, int kind)
//获取视频文件的缩略图,第一个参数为视频文件的位置,比如/sdcard/android123.3gp,而第二个参数可以为MINI_KIND或 MICRO_KIND最终和分辨率有关

2. static Bitmap extractThumbnail(Bitmap source, int width, int height, int options)
//直接对Bitmap进行缩略操作,最后一个参数定义为OPTIONS_RECYCLE_INPUT,来回收资源

3. static Bitmap extractThumbnail(Bitmap source, int width, int height)
// 这个和上面的方法一样,无options选项?
--------------------------------------------------
获取音频文件:
?Cursor cursor = getContentResolver().query( ?
???????????????? MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, ?
???????????????? MediaStore.Audio.Media.DEFAULT_SORT_ORDER); ?
?cursor.moveToFirst(); ?
int counter = cursor.getCount(); ?
String title = cursor.getString(cursor ?
???????????????? .getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE)); ?
? ?
Log.w("tag", "before looping,title=" + title); ?
for (int j = 0; j < counter; j++) { ?
??? Log.w("tag", "title=" + cursor.getString(cursor ?
???????????????????? .getColumnIndex(MediaStore.Audio.Media.TITLE))); ?
??? cursor.moveToNext(); ?
} ?
cursor.close();
---------------------------------------------------
增加,代码如下所以:

ContentValues values = new ContentValues(); ?
values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER,0); ?
resolver.insert(_uri, values); ?
ContentValues values = new ContentValues();
values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER,0);
resolver.insert(_uri, values);

这个insert传递的参数只有两个,一个是Uri(同查询那个Uri),另一个是ContentValues。这个ContentValuses对应于数据库的一行数据,只要用put方法把每个列的设置好之后,直接利用insert方
法去插入就好了。

更新,代码如下:

ContentResolver resolver = ctx.getContentResolver(); ?
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; ?
ContentValues values = new ContentValues(); ?
values.put(MediaStore.Audio.Media.DATE_MODIFIED, sid); ?
resolver.update(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,values, where, selectionArgs); ?
ContentResolver resolver = ctx.getContentResolver();
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
ContentValues values = new ContentValues();
values.put(MediaStore.Audio.Media.DATE_MODIFIED, sid);
resolver.update(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,values, where, selectionArgs);

上面update方法和查询还有增加里的参数都很类似,这里就不再重复叙述了,大家也可直接参考google的文档,那里也写的很清楚。

删除,代码如下:

ContentResolver resolver = ctx.getContentResolver(); ?
resolver.delete(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,where, selectionArgs); ?
ContentResolver resolver = ctx.getContentResolver();
resolver.delete(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,where, selectionArgs);

//--------------------------------------------
?* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
?* See the License for the specific language governing permissions and
?* limitations under the License.
?*/

package com.android.camera;

import com.android.camera.gallery.BaseCancelable;
import com.android.camera.gallery.BaseImageList;
import com.android.camera.gallery.Cancelable;
import com.android.camera.gallery.DrmImageList;
import com.android.camera.gallery.IImage;
import com.android.camera.gallery.IImageList;
import com.android.camera.gallery.Image;
import com.android.camera.gallery.ImageList;
import com.android.camera.gallery.ImageListUber;
import com.android.camera.gallery.SingleImageList;
import com.android.camera.gallery.VideoList;
import com.android.camera.gallery.VideoObject;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.location.Location;
import android.net.Uri;
import android.os.Environment;
import android.os.Parcel;
import android.provider.DrmStore;
import android.provider.MediaStore;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Images.ImageColumns;
import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.ExecutionException;

/**
?* ImageManager is used to retrieve and store images
?* in the media content provider.
?*/
public class ImageManager {
??? private static final String TAG = "ImageManager";

??? private static final Uri STORAGE_URI = Images.Media.EXTERNAL_CONTENT_URI;
??? private static final Uri THUMB_URI
??????????? = Images.Thumbnails.EXTERNAL_CONTENT_URI;

??? private static final Uri VIDEO_STORAGE_URI =
??????????? Uri.parse("content://media/external/video/media");

??? /**
???? * Enumerate type for the location of the images in gallery.
???? */
??? public static enum DataLocation { NONE, INTERNAL, EXTERNAL, ALL }

??? public static final Bitmap DEFAULT_THUMBNAIL =
??????????? Bitmap.createBitmap(32, 32, Bitmap.Config.RGB_565);
??? public static final Bitmap NO_IMAGE_BITMAP =
??????????? Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565);

??? public static final int SORT_ASCENDING = 1;
??? public static final int SORT_DESCENDING = 2;

??? public static final int INCLUDE_IMAGES = (1 << 0);
??? public static final int INCLUDE_DRM_IMAGES = (1 << 1);
??? public static final int INCLUDE_VIDEOS = (1 << 2);

??? public static final String CAMERA_IMAGE_BUCKET_NAME =
??????????? Environment.getExternalStorageDirectory().toString()
??????????? + "/DCIM/Camera";
??? public static final String CAMERA_IMAGE_BUCKET_ID =
??????????? getBucketId(CAMERA_IMAGE_BUCKET_NAME);

??? /**
???? * Matches code in MediaProvider.computeBucketValues. Should be a common
???? * function.
???? */
??? public static String getBucketId(String path) {
??????? return String.valueOf(path.toLowerCase().hashCode());
??? }

??? /**
???? * OSX requires plugged-in USB storage to have path /DCIM/NNNAAAAA to be
???? * imported. This is a temporary fix for bug#1655552.
???? */
??? public static void ensureOSXCompatibleFolder() {
??????? File nnnAAAAA = new File(
??????????? Environment.getExternalStorageDirectory().toString()
??????????? + "/DCIM/100ANDRO");
??????? if ((!nnnAAAAA.exists()) && (!nnnAAAAA.mkdir())) {
??????????? Log.e(TAG, "create NNNAAAAA file: " + nnnAAAAA.getPath()
??????????????????? + " failed");
??????? }
??? }

??? public static int roundOrientation(int orientationInput) {
??????? int orientation = orientationInput;
??????? if (orientation == -1) {
??????????? orientation = 0;
??????? }

??????? orientation = orientation % 360;
??????? int retVal;
??????? if (orientation < (0 * 90) + 45) {
??????????? retVal = 0;
??????? } else if (orientation < (1 * 90) + 45) {
??????????? retVal = 90;
??????? } else if (orientation < (2 * 90) + 45) {
??????????? retVal = 180;
??????? } else if (orientation < (3 * 90) + 45) {
??????????? retVal = 270;
??????? } else {
??????????? retVal = 0;
??????? }

??????? return retVal;
??? }

??? /**
???? * @return true if the mimetype is an image mimetype.
???? */
??? public static boolean isImageMimeType(String mimeType) {
??????? return mimeType.startsWith("image/");
??? }

??? /**
???? * @return true if the mimetype is a video mimetype.
???? */
??? public static boolean isVideoMimeType(String mimeType) {
??????? return mimeType.startsWith("video/");
??? }

??? /**
???? * @return true if the image is an image.
???? */
??? public static boolean isImage(IImage image) {
??????? return isImageMimeType(image.getMimeType());
??? }

??? /**
???? * @return true if the image is a video.
???? */
??? public static boolean isVideo(IImage image) {
??????? // This is the right implementation, but we use instanceof for speed.
??????? //return isVideoMimeType(image.getMimeType());
??????? return (image instanceof VideoObject);
??? }

??? public static void setImageSize(ContentResolver cr, Uri uri, long size) {
??????? ContentValues values = new ContentValues();
??????? values.put(Images.Media.SIZE, size);
??????? cr.update(uri, values, null, null);
??? }

??? public static Uri addImage(ContentResolver cr, String title,
??????????? long dateTaken, Location location,
??????????? int orientation, String directory, String filename) {

??????? ContentValues values = new ContentValues(7);
??????? values.put(Images.Media.TITLE, title);

??????? // That filename is what will be handed to Gmail when a user shares a
??????? // photo. Gmail gets the name of the picture attachment from the
??????? // "DISPLAY_NAME" field.
??????? values.put(Images.Media.DISPLAY_NAME, filename);
??????? values.put(Images.Media.DATE_TAKEN, dateTaken);
??????? values.put(Images.Media.MIME_TYPE, "image/jpeg");
??????? values.put(Images.Media.ORIENTATION, orientation);

??????? if (location != null) {
??????????? values.put(Images.Media.LATITUDE, location.getLatitude());
??????????? values.put(Images.Media.LONGITUDE, location.getLongitude());
??????? }

??????? if (directory != null && filename != null) {
??????????? String value = directory + "/" + filename;
??????????? values.put(Images.Media.DATA, value);
??????? }

??????? return cr.insert(STORAGE_URI, values);
??? }

??? private static class AddImageCancelable extends BaseCancelable<Void> {
??????? private final Uri mUri;
??????? private final ContentResolver mCr;
??????? private final int mOrientation;
??????? private final Bitmap mSource;
??????? private final byte [] mJpegData;

??????? public AddImageCancelable(Uri uri, ContentResolver cr,
??????????????? int orientation, Bitmap source, byte[] jpegData) {
??????????? if (source == null && jpegData == null || uri == null) {
??????????????? throw new IllegalArgumentException("source cannot be null");
??????????? }
??????????? mUri = uri;
??????????? mCr = cr;
??????????? mOrientation = orientation;
??????????? mSource = source;
??????????? mJpegData = jpegData;
??????? }

??????? @Override
??????? protected Void execute() throws InterruptedException,
??????????????? ExecutionException {
??????????? boolean complete = false;
??????????? try {
??????????????? long id = ContentUris.parseId(mUri);
??????????????? BaseImageList il = new ImageList(
??????????????????????? STORAGE_URI, THUMB_URI, SORT_ASCENDING, null);
??????????????? il.open(mCr);

??????????????? // TODO: Redesign the process of adding new images. We should
??????????????? //???? create an <code>IImage</code> in "ImageManager.addImage"
??????????????? //???? and pass the image object to here.
??????????????? Image image = new Image(il, mCr, id, 0, il.contentUri(id), null,
??????????????????????? 0, null, 0, null, null, 0);
??????????????? String[] projection = new String[] {
??????????????????????? ImageColumns._ID,
??????????????????????? ImageColumns.MINI_THUMB_MAGIC, ImageColumns.DATA};
??????????????? Cursor c = mCr.query(mUri, projection, null, null, null);
??????????????? String filepath;
??????????????? try {
??????????????????? c.moveToPosition(0);
??????????????????? filepath = c.getString(2);
??????????????? } finally {
??????????????????? c.close();
??????????????? }
??????????????? runSubTask(image.saveImageContents(
??????????????????????? mSource, mJpegData, mOrientation, true, filepath));

??????????????? ContentValues values = new ContentValues();
??????????????? values.put(ImageColumns.MINI_THUMB_MAGIC, 0);
??????????????? mCr.update(mUri, values, null, null);
??????????????? complete = true;
??????????????? return null;
??????????? } finally {
??????????????? if (!complete) {
??????????????????? try {
??????????????????????? mCr.delete(mUri, null, null);
??????????????????? } catch (Throwable t) {
??????????????????????? // ignore it while clean up.
??????????????????? }
??????????????? }
??????????? }
??????? }
??? }

??? public static Cancelable<Void> storeImage(
??????????? Uri uri, ContentResolver cr, int orientation,
??????????? Bitmap source, byte [] jpegData) {
??????? return new AddImageCancelable(
??????????????? uri, cr, orientation, source, jpegData);
??? }

??? public static IImageList makeImageList(Uri uri, ContentResolver cr,
??????????? int sort) {
??????? String uriString = (uri != null) ? uri.toString() : "";

??????? // TODO: we need to figure out whether we're viewing
??????? // DRM images in a better way.? Is there a constant
??????? // for content://drm somewhere??
??????? IImageList imageList;

??????? if (uriString.startsWith("content://drm")) {
??????????? imageList = ImageManager.allImages(
??????????????????? cr, ImageManager.DataLocation.ALL,
??????????????????? ImageManager.INCLUDE_DRM_IMAGES, sort);
??????? } else if (uriString.startsWith("content://media/external/video")) {
??????????? imageList = ImageManager.allImages(
??????????????? cr, ImageManager.DataLocation.EXTERNAL,
??????????????? ImageManager.INCLUDE_VIDEOS, sort);
??????? } else if (isSingleImageMode(uriString)) {
??????????? imageList = new SingleImageList(uri);
??????????? ((SingleImageList) imageList).open(cr);
??????? } else {
??????????? String bucketId = uri.getQueryParameter("bucketId");
??????????? imageList = ImageManager.allImages(
??????????????? cr, ImageManager.DataLocation.ALL,
??????????????? ImageManager.INCLUDE_IMAGES, sort, bucketId);
??????? }
??????? return imageList;
??? }

??? static boolean isSingleImageMode(String uriString) {
??????? return !uriString.startsWith(
??????????????? MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString())
??????????????? && !uriString.startsWith(
??????????????? MediaStore.Images.Media.INTERNAL_CONTENT_URI.toString());
??? }

??? private static class EmptyImageList implements IImageList {
??????? public static final Creator<EmptyImageList> CREATOR =
??????????????? new Creator<EmptyImageList>() {
??????????? public EmptyImageList createFromParcel(Parcel in) {
??????????????? return new EmptyImageList();
??????????? }

??????????? public EmptyImageList[] newArray(int size) {
??????????????? return new EmptyImageList[size];
??????????? }
??????? };

??????? public void open(ContentResolver resolver) {
??????? }

??????? public void close() {
??????? }

??????? public void checkThumbnail(int index) {
??????? }

??????? public void deactivate() {
??????? }

??????? public HashMap<String, String> getBucketIds() {
??????????? return new HashMap<String, String>();
??????? }

??????? public int getCount() {
??????????? return 0;
??????? }

??????? public boolean isEmpty() {
??????????? return true;
??????? }

??????? public IImage getImageAt(int i) {
??????????? return null;
??????? }

??????? public IImage getImageForUri(Uri uri) {
??????????? return null;
??????? }

??????? public boolean removeImage(IImage image) {
??????????? return false;
??????? }

??????? public boolean removeImageAt(int i) {
??????????? return false;
??????? }

??????? public int getImageIndex(IImage image) {
??????????? throw new UnsupportedOperationException();
??????? }

??????? public int describeContents() {
??????????? return 0;
??????? }

??????? public void writeToParcel(Parcel dest, int flags) {
??????? }
??? }

??? public static IImageList emptyImageList() {
??????? return new EmptyImageList();
??? }

??? public static IImageList allImages(ContentResolver cr,
??????????? DataLocation location, int inclusion, int sort) {
??????? return allImages(cr, location, inclusion, sort, null);
??? }

??? public static IImageList allImages(ContentResolver cr,
??????????? DataLocation location, int inclusion, int sort, String bucketId) {
??????? if (cr == null) {
??????????? return null;
??????? }

??????? // false ==> don't require write access
??????? boolean haveSdCard = hasStorage(false);

??????? // use this code to merge videos and stills into the same list
??????? ArrayList<BaseImageList> l = new ArrayList<BaseImageList>();

??????? if (haveSdCard && location != DataLocation.INTERNAL) {
??????????? if ((inclusion & INCLUDE_IMAGES) != 0) {
??????????????? l.add(new ImageList(
??????????????????????? STORAGE_URI, THUMB_URI, sort, bucketId));
??????????? }
??????????? if ((inclusion & INCLUDE_VIDEOS) != 0) {
??????????????? l.add(new VideoList(VIDEO_STORAGE_URI, sort, bucketId));
??????????? }
??????? }
??????? if (location == DataLocation.INTERNAL || location == DataLocation.ALL) {
??????????? if ((inclusion & INCLUDE_IMAGES) != 0) {
??????????????? l.add(new ImageList(
??????????????????????? Images.Media.INTERNAL_CONTENT_URI,
??????????????????????? Images.Thumbnails.INTERNAL_CONTENT_URI,
??????????????????????? sort, bucketId));
??????????? }
??????????? if ((inclusion & INCLUDE_DRM_IMAGES) != 0) {
??????????????? l.add(new DrmImageList(
??????????????????????? DrmStore.Images.CONTENT_URI, sort, bucketId));
??????????? }
??????? }

??????? // Optimization: If some of the lists are empty, remove them.
??????? // If there is only one remaining list, return it directly.
??????? Iterator<BaseImageList> iter = l.iterator();
??????? while (iter.hasNext()) {
??????????? BaseImageList sublist = iter.next();
??????????? sublist.open(cr);
??????????? if (sublist.isEmpty()) iter.remove();
??????????? sublist.close();
??????? }

??????? if (l.size() == 1) {
??????????? BaseImageList list = l.get(0);
??????????? list.open(cr);
??????????? return list;
??????? }

??????? ImageListUber uber = new ImageListUber(
??????????????? l.toArray(new IImageList[l.size()]), sort);
??????? uber.open(cr);
??????? return uber;
??? }

??? private static boolean checkFsWritable() {
??????? // Create a temporary file to see whether a volume is really writeable.
??????? // It's important not to put it in the root directory which may have a
??????? // limit on the number of files.
??????? String directoryName =
??????????????? Environment.getExternalStorageDirectory().toString() + "/DCIM";
??????? File directory = new File(directoryName);
??????? if (!directory.isDirectory()) {
??????????? if (!directory.mkdirs()) {
??????????????? return false;
??????????? }
??????? }
??????? File f = new File(directoryName, ".probe");
??????? try {
??????????? // Remove stale file if any
??????????? if (f.exists()) {
??????????????? f.delete();
??????????? }
??????????? if (!f.createNewFile()) {
??????????????? return false;
??????????? }
??????????? f.delete();
??????????? return true;
??????? } catch (IOException ex) {
??????????? return false;
??????? }
??? }

??? public static boolean hasStorage() {
??????? return hasStorage(true);
??? }

??? public static boolean hasStorage(boolean requireWriteAccess) {
??????? String state = Environment.getExternalStorageState();

??????? if (Environment.MEDIA_MOUNTED.equals(state)) {
??????????? if (requireWriteAccess) {
??????????????? boolean writable = checkFsWritable();
??????????????? return writable;
??????????? } else {
??????????????? return true;
??????????? }
??????? } else if (!requireWriteAccess
??????????????? && Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
??????????? return true;
??????? }
??????? return false;
??? }

??? private static Cursor query(ContentResolver resolver, Uri uri,
??????????? String[] projection, String selection, String[] selectionArgs,
??????????? String sortOrder) {
??????? try {
??????????? if (resolver == null) {
??????????????? return null;
??????????? }
??????????? return resolver.query(
??????????????????? uri, projection, selection, selectionArgs, sortOrder);
???????? } catch (UnsupportedOperationException ex) {
??????????? return null;
??????? }

??? }

??? public static boolean isMediaScannerScanning(ContentResolver cr) {
??????? boolean result = false;
??????? Cursor cursor = query(cr, MediaStore.getMediaScannerUri(),
??????????????? new String [] {MediaStore.MEDIA_SCANNER_VOLUME},
??????????????? null, null, null);
??????? if (cursor != null) {
??????????? if (cursor.getCount() == 1) {
??????????????? cursor.moveToFirst();
??????????????? result = "external".equals(cursor.getString(0));
??????????? }
??????????? cursor.close();
??????? }

??????? return result;
??? }

??? public static String getLastImageThumbPath() {
??????? return Environment.getExternalStorageDirectory().toString() +
?????????????? "/DCIM/.thumbnails/image_last_thumb";
??? }

??? public static String getLastVideoThumbPath() {
??????? return Environment.getExternalStorageDirectory().toString() +
?????????????? "/DCIM/.thumbnails/video_last_thumb";
??? }
}

//--------------------------------------------

禁止.thumbnails自动生成

~/.thumbnails
避免生成tumbnails文件夹的方法是在电脑上新建一个txt文件,并把这个文本文件的后缀名.txt去掉,电脑会提示“如果改变文件扩展名,可能导致文件不可用,确实要更改吗”这样的提示,点击确定,把得到的这个文件拷贝到TF卡的根目录,在安卓系统下将这个文件重命名为.nomedia(注意!nomedia单词前面有一个".")因windows系统不支持新建.与_开头的文件,所以重命名必须在WM系统下进行。按照以上步骤,DCIM文件夹下就不会再生成tumbnails文件夹。
~/.thumbnails
你打开看这个文件夹,是不是很奇怪。有好多你浏览过的图片,包括u盘等移动介质。还有预览的图片也在其中,随着时间可能达到1~2G
有些个人隐私,也跑这个文件夹中了。比如我喜欢使用 ecryptfs-ulit 一个商用级别(免费)的加密数据层。这下也白干了。
所以要对付它。删除它是不可能的,有些程序会自动生成它作为临时文件夹。可是却忘了删它……
/tmp是系统指定的临时文件夹,有个很好的特性——关闭系统会自动清空遮这个文件夹。
当然我把它扔到内存中,更保护硬盘。那只要启动前把/tmp 指定为tmpfs文件系统即可
把.thumbnails 删除了。复制/tmp文件夹 作为链接到~目录,命名为.thumbnails

热点排行