Angular或Three.js不会从服务器中提取图像,而是从浏览器缓存中提取图像

Angular or Three.js not pulling image from server, but rather browser cache

本文关键字:提取 图像 缓存 浏览器 服务器 Three js Angular      更新时间:2023-09-26

请参阅本文末尾的更新3获取最新活动/plunker

我在Node/Express应用程序上使用AngularJS。在这个应用程序中,我使用three.js来显示一个3D对象。包装该对象的纹理会改变,但文件名不会改变(例如,纹理图像的颜色会改变,但文件名仍然是Texture_0.png)。

纹理文件存储在Azure Blob Storage中,我启用了CORS,因此它可以正确显示。如果我在一个新的会话中更新一个图像并将其上传到Azure Blob Storage,那么我第一次渲染我的3D对象时,它会显示纹理的变化。但是,每当我上传新的更改并尝试重新渲染时,它都会显示浏览器缓存的图像,而不是存储在服务器中并引用的新图像。需要刷新页面才能看到更改,但我需要在单击弹出模式的按钮时显示此更改,而不是页面刷新。

如何让AngularJS显示服务器版本的纹理图像而不是浏览器缓存而不刷新我的页面?

我已经尝试添加一些随机数学来强制浏览器从服务器下载,但它不工作。相关代码如下:

Three.JS 3D Object Code:

$scope.generate3D = function () {
// 3D OBJECT - Variables
var texture0 = baseBlobURL + 'Texture_0.png?' + Math.random();
var boxDAE = baseBlobURL + 'Box.dae?' + Math.random();
var scene;
var camera;
var renderer;
var box;
var controls;
var newtexture;

// 3D OBJECT - Generate  
newtexture = THREE.ImageUtils.loadTexture(texture0);
//Instantiate a Collada loader
var loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true;
loader.load(boxDAE, function (collada) {
box = collada.scene;
box.traverse(function (child) {
if (child instanceof THREE.SkinnedMesh) {
var animation = new THREE.Animation(child, child.geometry.animation);
animation.play();
}
});
box.scale.x = box.scale.y = box.scale.z = .2;
box.updateMatrix();
init();
animate();
});
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xdddddd);
renderer.setSize(500, 500);
// Load the box file
scene.add(box);
// Lighting
var light = new THREE.AmbientLight();
scene.add(light);
// Camera
camera.position.x = 40;
camera.position.y = 40;
camera.position.z = 40;
camera.lookAt(scene.position);
// Rotation Controls
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.rotateSpeed = 5.0;
controls.zoomSpeed = 5;
controls.noZoom = false;
controls.noPan = false;
var myEl = angular.element(document.querySelector('#webGL-container'));
myEl.append(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);               
}
}

调用3D Object和Pop in Modal的Angular函数:

$scope.boxPreview = function () {
$scope.generate3D();
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: 'lg'
}

模态嵌入作为一个脚本标签(我需要这个在视图中,因为图像url是唯一的,代码运行我的视图需要这些变量以及3D对象代码):

<script type="text/ng-template" id="myModalContent.html">
    <div class="modal-header">
        <h3 class="modal-title">Box Preview</h3>
    </div>
    <div class="modal-body">
        <div id="webGL-container" width="500px">
        </div>
    </div>
    <div class="modal-footer">
        <button class="btn btn-primary" data-ng-click="ok()">Close</button>
    </div>
</script>
更新1:

在Chrome中查看网络选项卡时,我可以看到它正在拉下正确的图像,但只是没有在Modal中显示它。我试过禁用缓存,但是没有效果。就像three.js在某个地方保存了图像…但我只是不知道如何清除它。

更新2:

使用这篇文章中列出的Mr.doob的建议,我能够在得到以下错误之前重新加载它而不刷新页面一次:

RangeError: Maximum call stack size exceeded

当我在实例化加载器的地方添加以下代码时:

THREE.ImageLoader.prototype._load = THREE.ImageLoader.prototype.load;
THREE.ImageLoader.prototype.load = function (url, onLoad, onProgress, onError) {
this._load(url + '?' + Math.random(), onLoad, onProgress, onError);
};
//Instantiate a Collada loader
var loader = new THREE.ColladaLoader();

以下是事件的顺序:

1)在新会话中第一次点击显示3D框时,更改显示

2)更改纹理并上传,然后点击显示同一会话中的3D框,它会显示更改。以前,它不会显示,直到页面刷新。

3)更改纹理并上传,然后在同一会话中单击显示3D框。RangeError:最大调用堆栈大小超出

要清楚,我在模态中打开它。并且看到了模态产生这些错误的问题,除了我在路由级别只保留一个控制器调用。

更新3:

我把下面的东西放在一起:

http://plnkr.co/edit/DGZA9LUBZWsLWrmXaaKD

它正在使用我的dropbox公共文件夹获取图像。我得到同样的问题,如果我覆盖纹理文件,它不会更新,当我选择框预览,除非我刷新页面。我需要这个来刷新框的纹理点击。一些关键的注意事项:

1)我控制台记录了术语"加载",以表明即使在模态关闭后,这仍在继续渲染。我不确定是否需要持续渲染/动画,因为我只需要使用OrbitControls旋转盒子。请让我知道是否有更好的方法,或者这是否会导致缓存问题/我们需要销毁场景/重新创建它以便更新

2)使用Mr.Doob推荐的代码,我可以更新服务器上的图像,并在它给我一个错误之前正确地重新渲染一次:

Uncaught RangeError: Maximum call stack size exceeded

代码如下:

THREE.ImageLoader.prototype._load = THREE.ImageLoader.prototype.load;
            THREE.ImageLoader.prototype.load = function (url, onLoad, onProgress, onError) {
               this._load(url + '?' + Math.random(), onLoad, onProgress, onError);
           };

我保留了这段代码,因为它在崩溃之前至少完成了一次我想要的。

3)由于我们使用的是公共dropbox,我不确定那些帮助我的人是否能够覆盖Texture_0.png文件(我在油漆中打开它,只是在它上面涂了一些东西,然后重新上传到dropbox),但是你可以将url更改为你自己的dropbox进行测试,因为你有能力上传并看到盒子纹理不刷新,除非你重新加载网页(plunker)。

如果你正在编辑three.js背后的图像文件而不更改文件名,并且你想刷新材质纹理,你可以使用像这样的模式:

mesh.material.map.dispose(); // unrelated, but important!
mesh.material.map = THREE.ImageUtils.loadTexture( "filename.jpg" + "?" + Math.random() );

如果没有动画循环,你需要强制重新渲染,像这样:

mesh.material.map.dispose(); // unrelated, but important!
mesh.material.map = THREE.ImageUtils.loadTexture( "filename.jpg" + "?" + Math.random(), undefined, render );

three.js r.72

@weslangley回答对于three.js的大多数用户来说在技术上是正确的,因为他们使用的是材料。在我非常独特的情况下,我有一个独特的Collada文件,我必须编辑ImageLoader.js文件,以便让它从服务器上动态返回具有相同文件名(但不同图像内容)的图像文件。我不建议触摸源代码,但在我的情况下,我在ImageLoader.js中的load函数下添加了以下行:

url = url + '?' + Math.random();

对于我来说,这是有效的,但是对于three.js的99.9%的其他用户来说,这可能不适用。